<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>👍Tornado入门这一篇足以 | iworkh blog</title>
  
    <meta name="keywords" content=" tornado ">
  
  <meta name="description" content="👍Tornado入门这一篇足以 | iworkh blog">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="个人简介沐雨云楼，程序员一名。 喜欢研究技术，主要从事JAVA、微服务、大数据领域。 联系方式 QQ : 157162006 邮箱 :157162006@qq.com">
<meta property="og:type" content="website">
<meta property="og:title" content="关于">
<meta property="og:url" content="https://iworkh.gitee.io/blog/about/index.html">
<meta property="og:site_name" content="iworkh blog">
<meta property="og:description" content="个人简介沐雨云楼，程序员一名。 喜欢研究技术，主要从事JAVA、微服务、大数据领域。 联系方式 QQ : 157162006 邮箱 :157162006@qq.com">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-05-17T07:22:16.000Z">
<meta property="article:modified_time" content="2020-09-12T13:21:47.523Z">
<meta property="article:author" content="沐雨云楼">
<meta property="article:tag" content="iworkh">
<meta property="article:tag" content="沐雨云楼">
<meta property="article:tag" content="blog">
<meta name="twitter:card" content="summary">


<link rel="icon" href="/blog/img/iwork.svg">

<link href="/blog/css/style.css?v=1.0.1" rel="stylesheet">

<link href="/blog/css/hl_theme/atom-light.css?v=1.0.1" rel="stylesheet">

<link href="//cdn.bootcdn.net/ajax/libs/animate.css/4.1.0/animate.min.css" rel="stylesheet">
<link href="//cdn.bootcdn.net/ajax/libs/font-awesome/5.13.0/js/fontawesome.min.js" rel="stylesheet">

<script src="//cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="/blog/js/jquery.autocomplete.min.js?v=1.0.1" ></script>
<script src="/blog/js/titleTip.js?v=1.0.1" ></script>

<script src="//cdn.bootcdn.net/ajax/libs/highlight.js/10.0.3/highlight.min.js"></script>
<script>
    hljs.initHighlightingOnLoad();
</script>

<script src="//cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>



<script src="//cdn.bootcdn.net/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js" ></script>

<script src="/blog/js/iconfont.js?v=1.0.1" ></script>

<meta name="generator" content="Hexo 4.2.1"></head>
<div style="display: none">
  <input class="theme_disqus_on" value="false">
  <input class="theme_preload_comment" value="false">
  <input class="theme_blog_path" value="/blog">
</div>




<body>
<aside class="nav">
    <div class="nav-left">
        <a href="/blog/" class="avatar_target">
    <img class="avatar" src="/blog/img/iwork.svg" />
</a>
<div class="author">
    <span>沐雨云楼</span>
</div>

<div class="icon">
    
        
        <a title="github" href="https://gitee.com/iworkh" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-github"></use>
                </svg>
            
        </a>
        
    
        
        <a title="csdn" href="https://blog.csdn.net/u011622109" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-csdn"></use>
                </svg>
            
        </a>
        
    
        
        <a title="email" href="mailto:157162006@qq.com" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-email"></use>
                </svg>
            
        </a>
        
    
        
        <a title="qq" href="http://wpa.qq.com/msgrd?v=3&uin=157162006&site=qq&menu=yes" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-qq"></use>
                </svg>
            
        </a>
        
    
        
        <a title="kugou" href="https://www.kugou.com/" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-kugou"></use>
                </svg>
            
        </a>
        
    
        
        <a title="neteasemusic" href="https://music.163.com/" target="_blank">
            
                <svg class="iconfont-svg" aria-hidden="true">
                    <use xlink:href="#icon-neteasemusic"></use>
                </svg>
            
        </a>
        
    
</div>



<a class="more-menus">更多菜单</a>


<ul>
    <li><div class="all active">全部文章<small>(85)</small></div></li>
    
        
            
            <li><div data-rel="手册"><i class="fold iconfont icon-right"></i>手册<small>(2)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="iworkh">iworkh<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="pgmanor">pgmanor<small>(1)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="工具"><i class="fold iconfont icon-right"></i>工具<small>(16)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="git">git<small>(2)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="github">github<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="hexo">hexo<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="idea">idea<small>(2)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="markdowm">markdowm<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="maven">maven<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="有道云">有道云<small>(1)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="java"><i class="fold iconfont icon-right"></i>java<small>(20)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="dao">dao<small>(6)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="jvm">jvm<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="test">test<small>(5)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="tools">tools<small>(3)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="并发">并发<small>(1)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="微服务"><i class="fold iconfont icon-right"></i>微服务<small>(6)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="spring">spring<small>(2)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="springboot">springboot<small>(3)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="架构">架构<small>(2)</small></div>
                
            </li>
            
        
    
        
            
            <li><div data-rel="大数据"><i class="fold iconfont icon-right"></i>大数据<small>(4)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="hadoop">hadoop<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="spark">spark<small>(3)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="web"><i class="fold iconfont icon-right"></i>web<small>(3)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="vue">vue<small>(2)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="db"><i class="fold iconfont icon-right"></i>db<small>(4)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="mysql">mysql<small>(3)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="redis">redis<small>(1)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="ai">ai<small>(4)</small></div>
                
            </li>
            
        
    
        
            
            <li><div data-rel="python"><i class="fold iconfont icon-right"></i>python<small>(8)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="tornado">tornado<small>(3)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="linux"><i class="fold iconfont icon-right"></i>linux<small>(2)</small></div>
                
                    <ul class="sub hide">
                        
                        <li><div data-rel="docker">docker<small>(1)</small></div>
                            
                        </li>
                            
                        <li><div data-rel="nginx">nginx<small>(1)</small></div>
                            
                        </li>
                            
                    </ul>
                
            </li>
            
        
    
        
            
            <li><div data-rel="windows">windows<small>(3)</small></div>
                
            </li>
            
        
    
        
            
            <li><div data-rel="结构和算法">结构和算法<small>(8)</small></div>
                
            </li>
            
        
    
        
            
            <li><div data-rel="面试">面试<small>(2)</small></div>
                
            </li>
            
        
    
        
            
            <li><div data-rel="其他">其他<small>(1)</small></div>
                
            </li>
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
        
            
        
    
</ul>
<div class="left-bottom">
    <div class="menus">
    
    
    
    <a class="dynamic-menu " target="_blank"  href="https://pgmanor.gitee.io/blog/">pgmanor</a>
    
    
    <a class="dynamic-menu " target="_blank"  href="https://www.iworkh.com/manualIt/Category/scopeDev/">iworkh</a>
    
    
    <a class="dynamic-menu " target="_blank"  href="https://gitee.com/iworkh">gitee</a>
    
    </div>
    <div><a class="about  hasFriend  site_url"  href="/blog/about">关于</a><a style="width: 50%"  class="friends">友链</a></div>
</div>
<input type="hidden" id="iworkh_site_posts_number" value="85">
<input type="hidden" id="iworkh_site_word_count" value="197.8k">
<div style="display: none">
    <span id="busuanzi_value_site_uv"></span>
    <span id="busuanzi_value_site_pv"></span>
</div>

    </div>
    <div class="nav-right">
        <div class="friends-area">
    <div class="friends-title">
        友情链接
        <i class="back-title-list"></i>
    </div>
    <div class="friends-content">
        <ul>
            
            <li><a target="_blank" href="https://www.iworkh.com">iworkh</a></li>
            
            <li><a target="_blank" href="https://www.iworkh.com/download/share/">下载地址</a></li>
            
            <li><a target="_blank" href="http://yelog.org/">叶落阁</a></li>
            
        </ul>
    </div>
</div>
        <div class="title-list">
    <form onkeydown="if(event.keyCode === 13){return false;}">
        <input id="local-search-input" class="search" type="text" placeholder="in: 开头全文搜索" />
        <i class="cross"></i>
        <span>
            <label for="tagswitch">T:</label>
            <input id="tagswitch" type="checkbox" style="display: none" />
            <i id="tagsWitchIcon"></i>
        </span>
    </form>
    <div class="tags-list">
    
    <li class="article-tag-list-item">
        <a class="color3">ai</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">attr</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">aysnc</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">beanmap</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">bigdecimal</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">blog</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">chrome</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">dict</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">dll</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">docker</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">domain</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">fastjson</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">func</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">git</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">github</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">hadoop</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">hexo</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">idea</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">iworkh</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">jackson</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">java</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">jdni</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">jfinalshell</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">joplin</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">JPA</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">lock</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">markdowm</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">maven</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">mock</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">mq</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">mycat</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">mysql</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">nginx</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">notebook</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">plan</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">powermock</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color2">python</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">redis</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">rest</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">sharding-jdbc</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">spark</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">springboot</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">test</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">tool</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">tools</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">tornado</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">transactional</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">web</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">windows</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">wtforms</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">zookeeper</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">分库分表</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">分页</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">命令</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">工具</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">并发</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">序列化</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">微服务</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">性能分析</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">手册</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color4">有道云</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">标签</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">模板</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color5">百度云盘</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color1">结构和算法</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">计划</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">迁移</a>
    </li>
    
    <li class="article-tag-list-item">
        <a class="color3">面试</a>
    </li>
    
    <div class="clearfix"></div>
</div>

    
    <div id="local-search-result">

    </div>
    
    <nav id="title-list-nav">
        
        <a id="top" class="手册 pgmanor "
           href="/blog/2020/12/15/redirect-to-pgmanor/"
           data-tag="迁移"
           data-author="沐雨云楼" >
            <span class="post-title" title="😘博客迁移">😘博客迁移</span>
            <span class="post-date" title="2020-12-15 23:47:55">2020/12/15</span>
        </a>
        
        <a id="top" class="手册 iworkh "
           href="/blog/2020/05/27/manual-iworkh/"
           data-tag="手册,iworkh"
           data-author="沐雨云楼" >
            <span class="post-title" title="iworkh所有手册">iworkh所有手册</span>
            <span class="post-date" title="2020-05-27 21:37:04">2020/05/27</span>
        </a>
        
        <a id="top" class="微服务 springboot "
           href="/blog/2020/06/27/spring_boot_all_resources/"
           data-tag="springboot"
           data-author="沐雨云楼" >
            <span class="post-title" title="Spring Boot全网优质教程汇总">Spring Boot全网优质教程汇总</span>
            <span class="post-date" title="2020-06-27 17:43:13">2020/06/27</span>
        </a>
        
        <a id="top" class="工具 "
           href="/blog/2020/06/22/baidu-pandisk-share/"
           data-tag="百度云盘"
           data-author="沐雨云楼" >
            <span class="post-title" title="百度云盘分享链接">百度云盘分享链接</span>
            <span class="post-date" title="2020-06-22 18:38:21">2020/06/22</span>
        </a>
        
        <a id="top" class="工具 "
           href="/blog/2020/06/21/tool_blogs_all/"
           data-tag="blog"
           data-author="沐雨云楼" >
            <span class="post-title" title="优质博客和文章">优质博客和文章</span>
            <span class="post-date" title="2020-06-21 20:02:14">2020/06/21</span>
        </a>
        
        <a id="top" class="java jvm "
           href="/blog/2020/06/04/java-online-analysis/"
           data-tag="性能分析"
           data-author="沐雨云楼" >
            <span class="post-title" title="JAVA线上故障排查全套路">JAVA线上故障排查全套路</span>
            <span class="post-date" title="2020-06-04 20:34:05">2020/06/04</span>
        </a>
        
        <a id="top" class="工具 idea "
           href="/blog/2020/05/31/jetbrains-idea/"
           data-tag="工具,idea"
           data-author="沐雨云楼" >
            <span class="post-title" title="idea使用技巧">idea使用技巧</span>
            <span class="post-date" title="2020-05-31 16:12:30">2020/05/31</span>
        </a>
        
        <a id="top" class="工具 有道云 "
           href="/blog/2020/05/18/youdao_config_edit/"
           data-tag="工具,有道云"
           data-author="沐雨云楼" >
            <span class="post-title" title="有道云配置修改">有道云配置修改</span>
            <span class="post-date" title="2020-05-18 21:37:04">2020/05/18</span>
        </a>
        
        <a id="top" class="web "
           href="/blog/2020/06/01/web-tools/"
           data-tag="web,tool"
           data-author="沐雨云楼" >
            <span class="post-title" title="前端常用工具">前端常用工具</span>
            <span class="post-date" title="2020-06-01 21:37:04">2020/06/01</span>
        </a>
        
        <a  class="工具 "
           href="/blog/2020/10/25/tool-linux-jfinalshell/"
           data-tag="jfinalshell"
           data-author="沐雨云楼" >
            <span class="post-title" title="linux连接工具jfinalshell">linux连接工具jfinalshell</span>
            <span class="post-date" title="2020-10-25 10:24:59">2020/10/25</span>
        </a>
        
        <a  class="微服务 springboot "
           href="/blog/2020/10/06/spring-boot-multi-profiles/"
           data-tag="springboot"
           data-author="沐雨云楼" >
            <span class="post-title" title="springboot多环境配置打包">springboot多环境配置打包</span>
            <span class="post-date" title="2020-10-06 21:36:42">2020/10/06</span>
        </a>
        
        <a  class="web vue "
           href="/blog/2020/10/06/vue_springboot_package_publish/"
           data-tag="maven"
           data-author="沐雨云楼" >
            <span class="post-title" title="vue+springboot打包发布">vue+springboot打包发布</span>
            <span class="post-date" title="2020-10-06 14:22:29">2020/10/06</span>
        </a>
        
        <a  class="java dao "
           href="/blog/2020/10/06/java-jpa-query/"
           data-tag="JPA"
           data-author="沐雨云楼" >
            <span class="post-title" title="JPA自定义sql">JPA自定义sql</span>
            <span class="post-date" title="2020-10-06 10:20:38">2020/10/06</span>
        </a>
        
        <a  class="微服务 spring "
           href="/blog/2020/10/03/spring-pageable/"
           data-tag="分页"
           data-author="沐雨云楼" >
            <span class="post-title" title="vue+springboot分页交互">vue+springboot分页交互</span>
            <span class="post-date" title="2020-10-03 11:08:21">2020/10/03</span>
        </a>
        
        <a  class="工具 "
           href="/blog/2020/10/02/chrome-plugin/"
           data-tag="chrome"
           data-author="沐雨云楼" >
            <span class="post-title" title="chrome插件">chrome插件</span>
            <span class="post-date" title="2020-10-02 10:11:31">2020/10/02</span>
        </a>
        
        <a  class="windows "
           href="/blog/2020/10/01/windows-less-dll/"
           data-tag="dll"
           data-author="沐雨云楼" >
            <span class="post-title" title="windows缺少dll">windows缺少dll</span>
            <span class="post-date" title="2020-10-01 20:22:28">2020/10/01</span>
        </a>
        
        <a  class="java "
           href="/blog/2020/09/13/java-base-bigdecimal/"
           data-tag="bigdecimal"
           data-author="沐雨云楼" >
            <span class="post-title" title="BigDecimal正确使用了吗?">BigDecimal正确使用了吗?</span>
            <span class="post-date" title="2020-09-13 10:28:54">2020/09/13</span>
        </a>
        
        <a  class="windows "
           href="/blog/2020/09/12/windows-dev-install/"
           data-tag="windows"
           data-author="沐雨云楼" >
            <span class="post-title" title="windows开发环境安装">windows开发环境安装</span>
            <span class="post-date" title="2020-09-12 21:39:01">2020/09/12</span>
        </a>
        
        <a  class="工具 "
           href="/blog/2020/09/06/tool-notebook-joplin/"
           data-tag="joplin"
           data-author="沐雨云楼" >
            <span class="post-title" title="joplin笔记">joplin笔记</span>
            <span class="post-date" title="2020-09-06 20:50:55">2020/09/06</span>
        </a>
        
        <a  class="web vue "
           href="/blog/2020/09/05/vue-element-admin/"
           data-tag="web"
           data-author="沐雨云楼" >
            <span class="post-title" title="vue element admin开发">vue element admin开发</span>
            <span class="post-date" title="2020-09-05 11:01:38">2020/09/05</span>
        </a>
        
        <a  class="工具 "
           href="/blog/2020/09/05/tool-notebook/"
           data-tag="notebook"
           data-author="沐雨云楼" >
            <span class="post-title" title="常见笔记软件">常见笔记软件</span>
            <span class="post-date" title="2020-09-05 10:54:34">2020/09/05</span>
        </a>
        
        <a  class="java dao "
           href="/blog/2020/08/30/java-dao-shardingSphere/"
           data-tag="sharding-jdbc"
           data-author="沐雨云楼" >
            <span class="post-title" title="分库分表中间件-ShardingSphere">分库分表中间件-ShardingSphere</span>
            <span class="post-date" title="2020-08-30 21:52:45">2020/08/30</span>
        </a>
        
        <a  class="java dao "
           href="/blog/2020/08/30/java-dao-mycat/"
           data-tag="mycat"
           data-author="沐雨云楼" >
            <span class="post-title" title="分库分表中间件-MyCat">分库分表中间件-MyCat</span>
            <span class="post-date" title="2020-08-30 15:01:59">2020/08/30</span>
        </a>
        
        <a  class="java dao "
           href="/blog/2020/08/30/java-dao-sharding-db/"
           data-tag="分库分表"
           data-author="沐雨云楼" >
            <span class="post-title" title="分库分表中间件-开源产品">分库分表中间件-开源产品</span>
            <span class="post-date" title="2020-08-30 09:37:18">2020/08/30</span>
        </a>
        
        <a  class="java dao "
           href="/blog/2020/08/29/java-jndi/"
           data-tag="jdni"
           data-author="沐雨云楼" >
            <span class="post-title" title="JNDI">JNDI</span>
            <span class="post-date" title="2020-08-29 17:12:03">2020/08/29</span>
        </a>
        
        <a  class="工具 git "
           href="/blog/2020/08/27/tool-git-setting/"
           data-tag="git"
           data-author="沐雨云楼" >
            <span class="post-title" title="git配置">git配置</span>
            <span class="post-date" title="2020-08-27 21:30:17">2020/08/27</span>
        </a>
        
        <a  class="java test "
           href="/blog/2020/08/23/java-powermock/"
           data-tag="powermock"
           data-author="沐雨云楼" >
            <span class="post-title" title="powermock测试框架">powermock测试框架</span>
            <span class="post-date" title="2020-08-23 12:45:17">2020/08/23</span>
        </a>
        
        <a  class="java test "
           href="/blog/2020/08/23/java-mockito/"
           data-tag="mock"
           data-author="沐雨云楼" >
            <span class="post-title" title="👍mockito测试框架">👍mockito测试框架</span>
            <span class="post-date" title="2020-08-23 10:45:17">2020/08/23</span>
        </a>
        
        <a  class="java test "
           href="/blog/2020/08/22/java-test-ng/"
           data-tag="test"
           data-author="沐雨云楼" >
            <span class="post-title" title="👍TestNG一篇足以">👍TestNG一篇足以</span>
            <span class="post-date" title="2020-08-22 18:37:28">2020/08/22</span>
        </a>
        
        <a  class="db mysql "
           href="/blog/2020/08/19/mysql-common-sql/"
           data-tag="mysql"
           data-author="沐雨云楼" >
            <span class="post-title" title="mysql常用sql">mysql常用sql</span>
            <span class="post-date" title="2020-08-19 21:30:00">2020/08/19</span>
        </a>
        
        <a  class="db mysql "
           href="/blog/2020/08/19/mysql-windows-zip-install/"
           data-tag="mysql"
           data-author="沐雨云楼" >
            <span class="post-title" title="mysql zip在windows上安装">mysql zip在windows上安装</span>
            <span class="post-date" title="2020-08-19 21:00:47">2020/08/19</span>
        </a>
        
        <a  class="工具 idea "
           href="/blog/2020/08/15/jetbrains-crack/"
           data-tag="工具,idea"
           data-author="沐雨云楼" >
            <span class="post-title" title="jetbrains全家桶破解">jetbrains全家桶破解</span>
            <span class="post-date" title="2020-08-15 19:22:33">2020/08/15</span>
        </a>
        
        <a  class="工具 git "
           href="/blog/2020/07/10/tool-git-error-name-too-long/"
           data-tag="git"
           data-author="沐雨云楼" >
            <span class="post-title" title="git文件名太长">git文件名太长</span>
            <span class="post-date" title="2020-07-10 12:03:02">2020/07/10</span>
        </a>
        
        <a  class="db mysql "
           href="/blog/2020/07/04/mysql-lock-unlock-table/"
           data-tag="lock"
           data-author="沐雨云楼" >
            <span class="post-title" title="mysql数据库锁查询和释放">mysql数据库锁查询和释放</span>
            <span class="post-date" title="2020-07-04 21:46:54">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-sort/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法8-排序">数据结构和算法8-排序</span>
            <span class="post-date" title="2020-07-04 18:49:12">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-search/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法7-搜索">数据结构和算法7-搜索</span>
            <span class="post-date" title="2020-07-04 17:48:19">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-graph/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法6-非线性-图">数据结构和算法6-非线性-图</span>
            <span class="post-date" title="2020-07-04 16:09:47">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-tree/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法5-非线性-树">数据结构和算法5-非线性-树</span>
            <span class="post-date" title="2020-07-04 15:09:47">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-linear-queue/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法4-线性-队列">数据结构和算法4-线性-队列</span>
            <span class="post-date" title="2020-07-04 14:10:22">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-linear-stack/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法3-线性-栈">数据结构和算法3-线性-栈</span>
            <span class="post-date" title="2020-07-04 13:00:17">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-linear-list/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法2-线性-链">数据结构和算法2-线性-链</span>
            <span class="post-date" title="2020-07-04 11:32:12">2020/07/04</span>
        </a>
        
        <a  class="结构和算法 "
           href="/blog/2020/07/04/data-structures-algorithms-info/"
           data-tag="结构和算法"
           data-author="沐雨云楼" >
            <span class="post-title" title="数据结构和算法1-介绍">数据结构和算法1-介绍</span>
            <span class="post-date" title="2020-07-04 10:10:40">2020/07/04</span>
        </a>
        
        <a  class="大数据 spark "
           href="/blog/2020/06/30/spark-install-windows/"
           data-tag="spark"
           data-author="沐雨云楼" >
            <span class="post-title" title="spark开发环境搭建">spark开发环境搭建</span>
            <span class="post-date" title="2020-06-30 19:24:51">2020/06/30</span>
        </a>
        
        <a  class="大数据 hadoop "
           href="/blog/2020/06/30/hadoop-install-windows/"
           data-tag="hadoop"
           data-author="沐雨云楼" >
            <span class="post-title" title="hadoop开发环境搭建">hadoop开发环境搭建</span>
            <span class="post-date" title="2020-06-30 18:06:23">2020/06/30</span>
        </a>
        
        <a  class="java "
           href="/blog/2020/06/30/java-install-windows/"
           data-tag="java"
           data-author="沐雨云楼" >
            <span class="post-title" title="jdk开发环境搭建">jdk开发环境搭建</span>
            <span class="post-date" title="2020-06-30 15:09:34">2020/06/30</span>
        </a>
        
        <a  class="ai "
           href="/blog/2020/06/30/ai-python-spark-env/"
           data-tag="spark"
           data-author="沐雨云楼" >
            <span class="post-title" title="python spark开发环境搭建">python spark开发环境搭建</span>
            <span class="post-date" title="2020-06-30 12:14:04">2020/06/30</span>
        </a>
        
        <a  class="ai "
           href="/blog/2020/06/29/ai-jupyter-notebook/"
           data-tag="标签"
           data-author="沐雨云楼" >
            <span class="post-title" title="jupyter notebook">jupyter notebook</span>
            <span class="post-date" title="2020-06-29 12:29:36">2020/06/29</span>
        </a>
        
        <a  class="ai "
           href="/blog/2020/06/26/ai-python-tools/"
           data-tag="tools"
           data-author="沐雨云楼" >
            <span class="post-title" title="python和ai常用工具">python和ai常用工具</span>
            <span class="post-date" title="2020-06-26 09:55:24">2020/06/26</span>
        </a>
        
        <a  class="python "
           href="/blog/2020/06/25/python-tool-buildin-attr/"
           data-tag="attr"
           data-author="沐雨云楼" >
            <span class="post-title" title="👍python内置属性(魔法方法)">👍python内置属性(魔法方法)</span>
            <span class="post-date" title="2020-06-25 11:01:33">2020/06/25</span>
        </a>
        
        <a  class="python "
           href="/blog/2020/06/24/python-tool-buildin-func/"
           data-tag="func"
           data-author="沐雨云楼" >
            <span class="post-title" title="👍python内置函数">👍python内置函数</span>
            <span class="post-date" title="2020-06-24 18:31:59">2020/06/24</span>
        </a>
        
        <a  class="python "
           href="/blog/2020/06/24/python-tool-obj-dict/"
           data-tag="dict"
           data-author="沐雨云楼" >
            <span class="post-title" title="python对象与dict互转">python对象与dict互转</span>
            <span class="post-date" title="2020-06-24 18:15:03">2020/06/24</span>
        </a>
        
        <a  class="大数据 spark "
           href="/blog/2020/06/21/spark-version-3/"
           data-tag="spark"
           data-author="沐雨云楼" >
            <span class="post-title" title="spark3.0新特性">spark3.0新特性</span>
            <span class="post-date" title="2020-06-21 17:40:12">2020/06/21</span>
        </a>
        
        <a  class="java "
           href="/blog/2020/06/21/java-base-domain/"
           data-tag="domain"
           data-author="沐雨云楼" >
            <span class="post-title" title="VO DTO DO PO你都了解吗?">VO DTO DO PO你都了解吗?</span>
            <span class="post-date" title="2020-06-21 09:20:03">2020/06/21</span>
        </a>
        
        <a  class="大数据 spark "
           href="/blog/2020/06/20/spark-data-skew/"
           data-tag="spark"
           data-author="沐雨云楼" >
            <span class="post-title" title="spark数据倾斜及解决方案">spark数据倾斜及解决方案</span>
            <span class="post-date" title="2020-06-20 21:41:12">2020/06/20</span>
        </a>
        
        <a  class="linux nginx "
           href="/blog/2020/06/20/nginx-fast/"
           data-tag="nginx"
           data-author="沐雨云楼" >
            <span class="post-title" title="nginx为何这么快?">nginx为何这么快?</span>
            <span class="post-date" title="2020-06-20 17:55:10">2020/06/20</span>
        </a>
        
        <a  class="架构 "
           href="/blog/2020/06/20/fw-mq-message-missing/"
           data-tag="mq"
           data-author="沐雨云楼" >
            <span class="post-title" title="MQ如何保证消息不丢失?">MQ如何保证消息不丢失?</span>
            <span class="post-date" title="2020-06-20 17:03:18">2020/06/20</span>
        </a>
        
        <a  class="java "
           href="/blog/2020/06/20/java-base-serializable/"
           data-tag="序列化"
           data-author="沐雨云楼" >
            <span class="post-title" title="java序列化">java序列化</span>
            <span class="post-date" title="2020-06-20 16:29:04">2020/06/20</span>
        </a>
        
        <a  class="架构 "
           href="/blog/2020/06/20/fw-distributed-lock-redis-zk/"
           data-tag="lock,redis,zookeeper"
           data-author="沐雨云楼" >
            <span class="post-title" title="分布式锁用redis还是zk?">分布式锁用redis还是zk?</span>
            <span class="post-date" title="2020-06-20 12:09:55">2020/06/20</span>
        </a>
        
        <a  class="java tools "
           href="/blog/2020/06/19/java-tool-jackson/"
           data-tag="jackson"
           data-author="沐雨云楼" >
            <span class="post-title" title="工具类--jackson">工具类--jackson</span>
            <span class="post-date" title="2020-06-19 13:00:45">2020/06/19</span>
        </a>
        
        <a  class="java tools "
           href="/blog/2020/06/19/java-tool-bean-map/"
           data-tag="beanmap"
           data-author="沐雨云楼" >
            <span class="post-title" title="工具类--bean和map互转">工具类--bean和map互转</span>
            <span class="post-date" title="2020-06-19 12:30:14">2020/06/19</span>
        </a>
        
        <a  class="java test "
           href="/blog/2020/06/18/java-rest-assured-in-action/"
           data-tag="rest"
           data-author="沐雨云楼" >
            <span class="post-title" title="rest-assured实战">rest-assured实战</span>
            <span class="post-date" title="2020-06-18 17:51:56">2020/06/18</span>
        </a>
        
        <a  class="java test "
           href="/blog/2020/06/17/java_rest_assured_wiki_info/"
           data-tag="rest"
           data-author="沐雨云楼" >
            <span class="post-title" title="rest-assured wiki翻译">rest-assured wiki翻译</span>
            <span class="post-date" title="2020-06-17 20:39:11">2020/06/17</span>
        </a>
        
        <a  class="python tornado "
           href="/blog/2020/06/16/tornado-wtforms/"
           data-tag="wtforms"
           data-author="沐雨云楼" >
            <span class="post-title" title="wtforms表单验证">wtforms表单验证</span>
            <span class="post-date" title="2020-06-16 21:52:33">2020/06/16</span>
        </a>
        
        <a  class="python tornado "
           href="/blog/2020/06/16/tornado-sync-to-async/"
           data-tag="aysnc"
           data-author="沐雨云楼" >
            <span class="post-title" title="tornado同步转异步">tornado同步转异步</span>
            <span class="post-date" title="2020-06-16 21:50:25">2020/06/16</span>
        </a>
        
        <a  class="python tornado "
           href="/blog/2020/06/08/python_tornado_info/"
           data-tag="tornado"
           data-author="沐雨云楼" >
            <span class="post-title" title="👍Tornado入门这一篇足以">👍Tornado入门这一篇足以</span>
            <span class="post-date" title="2020-06-08 20:31:32">2020/06/08</span>
        </a>
        
        <a  class="工具 maven "
           href="/blog/2020/06/07/tool-maven/"
           data-tag="tools,工具"
           data-author="沐雨云楼" >
            <span class="post-title" title="maven常用命令和配置">maven常用命令和配置</span>
            <span class="post-date" title="2020-06-07 16:30:11">2020/06/07</span>
        </a>
        
        <a  class="微服务 springboot "
           href="/blog/2020/06/07/spring-initialzr/"
           data-tag="tools,工具"
           data-author="沐雨云楼" >
            <span class="post-title" title="spring initialzr环境编译发布">spring initialzr环境编译发布</span>
            <span class="post-date" title="2020-06-07 15:06:00">2020/06/07</span>
        </a>
        
        <a  class="windows "
           href="/blog/2020/06/07/windows-cmd/"
           data-tag="tools,工具,命令"
           data-author="沐雨云楼" >
            <span class="post-title" title="windows常用命令和技巧">windows常用命令和技巧</span>
            <span class="post-date" title="2020-06-07 11:39:01">2020/06/07</span>
        </a>
        
        <a  class="工具 github "
           href="/blog/2020/06/07/tool-connect-github/"
           data-tag="github"
           data-author="沐雨云楼" >
            <span class="post-title" title="github无法访问解决办法">github无法访问解决办法</span>
            <span class="post-date" title="2020-06-07 10:08:38">2020/06/07</span>
        </a>
        
        <a  class="微服务 spring "
           href="/blog/2020/06/03/spring-transaction/"
           data-tag="transactional"
           data-author="沐雨云楼" >
            <span class="post-title" title="Transactional注解">Transactional注解</span>
            <span class="post-date" title="2020-06-03 22:09:42">2020/06/03</span>
        </a>
        
        <a  class="java dao "
           href="/blog/2020/06/03/java-jpa-query-multi-param/"
           data-tag="JPA"
           data-author="沐雨云楼" >
            <span class="post-title" title="JPA多条件查询">JPA多条件查询</span>
            <span class="post-date" title="2020-06-03 21:38:04">2020/06/03</span>
        </a>
        
        <a  class="java tools "
           href="/blog/2020/06/02/java-tool-fastjson-bug/"
           data-tag="fastjson"
           data-author="沐雨云楼" >
            <span class="post-title" title="Fastjson远程代码高危漏洞">Fastjson远程代码高危漏洞</span>
            <span class="post-date" title="2020-06-02 22:38:28">2020/06/02</span>
        </a>
        
        <a  class="linux docker "
           href="/blog/2020/06/02/docker-introduction/"
           data-tag="docker"
           data-author="沐雨云楼" >
            <span class="post-title" title="👍docker入门看这一篇就够了">👍docker入门看这一篇就够了</span>
            <span class="post-date" title="2020-06-02 22:22:30">2020/06/02</span>
        </a>
        
        <a  class="面试 "
           href="/blog/2020/06/02/interview-java-question/"
           data-tag="面试"
           data-author="沐雨云楼" >
            <span class="post-title" title="java常见面试题">java常见面试题</span>
            <span class="post-date" title="2020-06-02 22:15:48">2020/06/02</span>
        </a>
        
        <a  class="微服务 "
           href="/blog/2020/06/02/microservice-why/"
           data-tag="微服务"
           data-author="沐雨云楼" >
            <span class="post-title" title="牛逼公司一定要使用微服务？">牛逼公司一定要使用微服务？</span>
            <span class="post-date" title="2020-06-02 21:15:29">2020/06/02</span>
        </a>
        
        <a  class="面试 "
           href="/blog/2020/06/02/interview-company/"
           data-tag="面试"
           data-author="沐雨云楼" >
            <span class="post-title" title="IT互联网公司名单">IT互联网公司名单</span>
            <span class="post-date" title="2020-06-02 20:41:23">2020/06/02</span>
        </a>
        
        <a  class="java 并发 "
           href="/blog/2020/06/02/java-concurrent-aqs/"
           data-tag="java,并发"
           data-author="沐雨云楼" >
            <span class="post-title" title="java并发AQS">java并发AQS</span>
            <span class="post-date" title="2020-06-02 20:01:48">2020/06/02</span>
        </a>
        
        <a  class="ai "
           href="/blog/2020/05/30/ai-learn-catalog/"
           data-tag="ai"
           data-author="沐雨云楼" >
            <span class="post-title" title="AI学习目录">AI学习目录</span>
            <span class="post-date" title="2020-05-30 08:49:39">2020/05/30</span>
        </a>
        
        <a  class="工具 hexo "
           href="/blog/2020/05/29/hexo-theme-math/"
           data-tag="工具,hexo"
           data-author="沐雨云楼" >
            <span class="post-title" title="hexo主题支持MathJax">hexo主题支持MathJax</span>
            <span class="post-date" title="2020-05-29 09:36:46">2020/05/29</span>
        </a>
        
        <a  class="db redis "
           href="/blog/2020/05/28/redis-memory-eliminate/"
           data-tag="redis"
           data-author="沐雨云楼" >
            <span class="post-title" title="Redis的内存淘汰策略">Redis的内存淘汰策略</span>
            <span class="post-date" title="2020-05-28 21:37:04">2020/05/28</span>
        </a>
        
        <a  class="工具 markdowm "
           href="/blog/2020/05/28/markdown-math-express/"
           data-tag="工具,markdowm"
           data-author="沐雨云楼" >
            <span class="post-title" title="markdown数学公式">markdown数学公式</span>
            <span class="post-date" title="2020-05-28 08:02:03">2020/05/28</span>
        </a>
        
        <a  class="python "
           href="/blog/2020/05/21/python_install/"
           data-tag="python"
           data-author="沐雨云楼" >
            <span class="post-title" title="python环境安装">python环境安装</span>
            <span class="post-date" title="2020-05-21 21:37:04">2020/05/21</span>
        </a>
        
        <a  class="python "
           href="/blog/2020/05/21/python_base/"
           data-tag="python"
           data-author="沐雨云楼" >
            <span class="post-title" title="python基本语法">python基本语法</span>
            <span class="post-date" title="2020-05-21 00:00:00">2020/05/21</span>
        </a>
        
        <a  class="其他 "
           href="/blog/2020/05/20/todo-list/"
           data-tag="plan,计划"
           data-author="沐雨云楼" >
            <span class="post-title" title="计划">计划</span>
            <span class="post-date" title="2020-05-20 11:04:19">2020/05/20</span>
        </a>
        
        <a  class="工具 "
           href="/blog/2020/05/20/blog-template/"
           data-tag="模板"
           data-author="沐雨云楼" >
            <span class="post-title" title="博客自定义模板">博客自定义模板</span>
            <span class="post-date" title="2020-05-20 05:20:00">2020/05/20</span>
        </a>
        
    </nav>
</div>

    </div>
    <div class="hide-list">
        <div class="semicircle" data-title="切换全屏 快捷键 s">
            <div class="brackets first"><</div>
            <div class="brackets">&gt;</div>
        </div>
    </div>
</aside>
<div class="post">
    <div class="pjax">
        <article id="post-python-tornado-info" class="article article-type-post" itemscope itemprop="blogPost">
    
        <h1 class="article-title">👍Tornado入门这一篇足以</h1>
    
    <div class="article-meta">
        
        
        <span class="author"><a>沐雨云楼</a></span>
        
        
        <span class="book">
            
                <a  data-rel="python">python</a>/
            
                <a  data-rel="tornado">tornado</a>
            
        </span>
        
        
        <span class="tag">
            
            <a class="color3">tornado</a>
            
        </span>
        
    </div>
    <div class="article-meta">
        
            发布时间 : <time class="date" title='最后更新: 2020-09-12 21:21:47'>2020-06-08 20:31</time>
        
    </div>
    <div class="article-meta">
        
        <span>字数:11.7k</span>
        
        
        <span id="busuanzi_container_page_pv">
            阅读 :<span id="busuanzi_value_page_pv">
                <span class="count-comment">
                    <span class="spinner">
                      <div class="cube1"></div>
                      <div class="cube2"></div>
                    </span>
                </span>
            </span>
        </span>
        
        
        <span class="top-comment" title="跳转至评论区">
            <a href="#comments">
                评论:<span class="count-comment">
                    <span class="spinner">
                      <div class="cube1"></div>
                      <div class="cube2"></div>
                    </span>
                </span>
            </a>
        </span>
        
    </div>
    
    <div class="toc-ref">
    
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-简介"><span class="toc-text">1.简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-架构"><span class="toc-text">2.架构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#2-1-分层"><span class="toc-text">2-1.分层</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-2-模块"><span class="toc-text">2-2.模块</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-3-请求流程"><span class="toc-text">2-3.请求流程</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-安装"><span class="toc-text">3.安装</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#3-1-python环境安装"><span class="toc-text">3-1.python环境安装</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-2-开发工具"><span class="toc-text">3-2.开发工具</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-3-pip使用"><span class="toc-text">3-3.pip使用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-4-安装tornado"><span class="toc-text">3-4.安装tornado</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-5-创建项目"><span class="toc-text">3-5.创建项目</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-tornado优势"><span class="toc-text">4.tornado优势</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#4-1-web框架比较"><span class="toc-text">4-1.web框架比较</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-2-如何高并发"><span class="toc-text">4-2.如何高并发</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-3-建议"><span class="toc-text">4-3.建议</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-tornado异步编程"><span class="toc-text">5.tornado异步编程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#5-1-io模型"><span class="toc-text">5-1.io模型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-2-io多路复用"><span class="toc-text">5-2.io多路复用</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#5-2-1-select"><span class="toc-text">5-2-1.select</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-2-3-poll"><span class="toc-text">5-2-3.poll</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-2-4-epoll"><span class="toc-text">5-2-4.epoll</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-2-5-回调异步"><span class="toc-text">5-2-5.回调异步</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-3-协程"><span class="toc-text">5-3.协程</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#5-3-1-yield生成器"><span class="toc-text">5-3-1.yield生成器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-3-2-异步"><span class="toc-text">5-3-2.异步</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#5-3-3-执行"><span class="toc-text">5-3-3.执行</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-4-AsnycHttpClient"><span class="toc-text">5-4.AsnycHttpClient</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-tornado-web基础"><span class="toc-text">6.tornado web基础</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#6-1-简单示例"><span class="toc-text">6-1.简单示例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-2-url匹配"><span class="toc-text">6-2.url匹配</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-3-动态传参"><span class="toc-text">6-3.动态传参</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#6-3-1-命令方式"><span class="toc-text">6-3-1.命令方式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-3-2-文件方式"><span class="toc-text">6-3-2.文件方式</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-4-handler"><span class="toc-text">6-4.handler</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#6-4-1-input"><span class="toc-text">6-4-1.input</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-4-2-process"><span class="toc-text">6-4-2.process</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-4-3-output"><span class="toc-text">6-4-3.output</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-5-setting"><span class="toc-text">6-5.setting</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6-6-模板"><span class="toc-text">6-6.模板</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#6-6-1-页面结构"><span class="toc-text">6-6-1.页面结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-6-1-结果"><span class="toc-text">6-6-1.结果</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#6-6-2-代码"><span class="toc-text">6-6-2.代码</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-mysql"><span class="toc-text">7.mysql</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#7-1-简单示例"><span class="toc-text">7-1.简单示例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7-1-查询数据"><span class="toc-text">7-1.查询数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7-2-插入数据"><span class="toc-text">7-2.插入数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7-3-更新数据"><span class="toc-text">7-3.更新数据</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-orm框架peewee"><span class="toc-text">8.orm框架peewee</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#8-1-orm好处"><span class="toc-text">8-1.orm好处</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-2-peewee同步操作"><span class="toc-text">8-2.peewee同步操作</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-1-定义model"><span class="toc-text">8-2-1.定义model</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-2-初始化表"><span class="toc-text">8-2-2.初始化表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-3-插入"><span class="toc-text">8-2-3.插入</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-4-查询"><span class="toc-text">8-2-4.查询</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-5-更新"><span class="toc-text">8-2-5.更新</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-6-删除"><span class="toc-text">8-2-6.删除</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-2-7-技巧"><span class="toc-text">8-2-7.技巧</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-3-peewee-async操作"><span class="toc-text">8-3.peewee-async操作</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#8-3-1-定义models"><span class="toc-text">8-3-1.定义models</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#8-3-2-基本操作"><span class="toc-text">8-3-2.基本操作</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-参考"><span class="toc-text">9.参考</span></a></li></ol>
    
<style>
    .left-col .switch-btn,
    .left-col .switch-area {
        display: none;
    }
    .toc-level-4 i,
    .toc-level-4 ol {
        display: none !important;
    }
</style>
</div>
    
    <div class="article-entry" itemprop="articleBody">
      
        <ul>
<li><a href="https://www.tornadoweb.org/en/stable/index.html" target="_blank" rel="noopener">tornado官网文档</a></li>
<li><a href="https://tornado-zh.readthedocs.io/zh/latest/" target="_blank" rel="noopener">tornado中文文档</a></li>
<li><a href="http://shouce.jb51.net/tornado/" target="_blank" rel="noopener">tornado书籍</a></li>
</ul>
<blockquote>
<p>内容虽有点比较多，只要耐心看完必定有所收获。</p>
</blockquote>
<h2 id="1-简介"><a href="#1-简介" class="headerlink" title="1.简介"></a>1.简介</h2><p>Tornado是一个python语言的web服务框架，它是基于社交聚合网站FriendFeed的实时信息服务开发而来的。</p>
<blockquote>
<p>2007年由4名Google前软件工程师一起创办了FriendFeed，旨在使用户能够方便地跟踪好友在Facebook和Twitter等多个社交网站上的活动。结果两年后，Facebook宣布收购FriendFeed</p>
</blockquote>
<p>Tornado使FriendFeed使用的可扩展的非阻塞Web服务器及其相关工具的开源版本，这个Web框架看起来有些像web.py或 Google的webapp，不过为了更加有效地利用非阻塞服务器环境，Tornado这个Web框架还包含了一些相关的有用工具和优化。</p>
<p><strong>与其他web框架一些区别</strong></p>
<ul>
<li>非阻塞式的服务器<blockquote>
<p>速度相当快，使用了epoll非阻塞的方式，每秒可以处理数以千计的连接。</p>
</blockquote>
</li>
<li>单进程单线程异步IO的网络模型</li>
</ul>
<p><strong>特点</strong></p>
<ul>
<li>轻量级Web框架</li>
<li>异步非阻塞IO处理方式(epoll)<blockquote>
<p>单进程单线程异步IO的网络模式, epoll的异步网络IO</p>
</blockquote>
</li>
<li>出色的抗负载能力</li>
<li>不依赖多进程或多线程(协程机制)</li>
<li>WSGI全栈替代产品<blockquote>
<p>WSGI把应用（Application）和服务器（Server）结合起来，Tornado既可以是WSGI应用也可以是WSGI服务。<br>既是WebServer也是WebFramework</p>
</blockquote>
</li>
<li>支持长连接</li>
<li>AysncIo</li>
<li>http异步客户端AsyncHttpClient</li>
</ul>
<h2 id="2-架构"><a href="#2-架构" class="headerlink" title="2.架构"></a>2.架构</h2><blockquote>
<p>可以先跳过架构这块介绍，到安装使用。（使用过了tornado，再来看整体架构就清晰容易多了）</p>
</blockquote>
<h3 id="2-1-分层"><a href="#2-1-分层" class="headerlink" title="2-1.分层"></a>2-1.分层</h3><p>Tornado不仅仅是一个Web框架，它完整地实现了HTTP服务器和客户端，再此基础上提供了Web服务，它可分为四层：</p>
<ul>
<li><strong>Web框架</strong>：最上层，包括处理器、模板、数据库连接、认证、本地化等Web框架所需功能。</li>
<li><strong>HTTP/HTTPS层</strong>：基于HTTP协议实现了HTTP服务器和客户端</li>
<li><strong>TCP层</strong>：实现TCP服务器负责数据传输</li>
<li><strong>Event层</strong>：最底层、处理IO事件</li>
</ul>
<img src="/blog/images/python/tornado/tornado_arc_component.png" width="800px">

<blockquote>
<p>注意颜色分层</p>
</blockquote>
<ul>
<li>httpserver: 服务于web模块的一个简单的HTTP服务器的实现<blockquote>
<p>Tornado的HTTPConnection类用来处理HTTP请求，包括读取HTTP请求头、读取POST传递的数据，调用用户自定义的处理方法，以及把响应数据写给客户端的socket。</p>
</blockquote>
</li>
<li>iostream: 对非阻塞式的socket的封装以便于常见读写操作<blockquote>
<p>为了在处理请求时实现对socket的异步读写，Tornado实现了IOStream类用来处理socket的异步读写。</p>
</blockquote>
</li>
<li>ioloop 核心的I/O循环<blockquote>
<p>Tornado为了实现高并发和高性能，使用了一个IOLoop事件循环来处理socket的读写事件，IOLoop事件循环是基于Linux的epoll模型，可以高效地响应网络事件，这是Tornado高效的基础保证。</p>
</blockquote>
</li>
</ul>
<h3 id="2-2-模块"><a href="#2-2-模块" class="headerlink" title="2-2.模块"></a>2-2.模块</h3><p>Tornado是一个轻量级框架，它的模块不多最重要的模块是web，web模块包含了Tornado大部分主要功能的Web框架，其他模块都是工具性质的，以便让Web模块更加有用。</p>
<p><strong>Core Web Framework 核心Web框架</strong></p>
<ul>
<li>tornado.web 包括Web框架大部分主要功能，包括RequestHandler和Application类。</li>
<li>tornado.httpserver一个无阻塞HTTP服务器的实现</li>
<li>tornado.template模板系统</li>
<li>tornado.escape HTML、JSON、URLs等编码解码和字符串操作</li>
<li>tornado.locale国际化支持</li>
</ul>
<p><strong>Asynchronous Networking 异步网络底层模块</strong></p>
<ul>
<li>tornado.ioloop 核心IO循环</li>
<li>tornado.iostream对非阻塞的Socket的简单封装以方便常用读写操作</li>
<li>tornado.httpclient无阻塞的HTTP服务器实现</li>
<li>tornado.netutil网络应用的实现主要是TCPServer类</li>
</ul>
<p><strong>Integration With Other Services 系统集成服务</strong></p>
<ul>
<li>tornado.auth 使用OpenId和OAuth进行第三方登录</li>
<li>tornado.databaseMySQL服务端封装</li>
<li>tornado.platform.twisted在Tornado上运行Twisted实现的代码</li>
<li>tornado.websocket实现和浏览器的双向通信</li>
<li>tornado.wsgi其他Python网络框架或服务器的相互操作</li>
</ul>
<p><strong>Utilities 应用模块</strong></p>
<ul>
<li>tornado.autoload产生环境中自动检查代码更新</li>
<li>tornado.gen基于生成器的接口，使用该模块 保证代码异步运行。</li>
<li>tornado.httputil分析HTTP请求内容</li>
<li>tornado.options解析终端参数</li>
<li>tornado.process多进程实现的封装</li>
<li>tornado.stack_context异步环境中对回调函数上下文保存、异常处理</li>
<li>tornado.testing单元测试</li>
</ul>
<h3 id="2-3-请求流程"><a href="#2-3-请求流程" class="headerlink" title="2-3.请求流程"></a>2-3.请求流程</h3><p>注意请求和返回的流程（颜色区别，以IO Loop为起点）<br><img src="/blog/images/python/tornado/tornado_request_flow_simple.png" width="650px"></p>
<p><img src="/blog/images/python/tornado/tornado_request_flow_detial.png" alt="img"></p>
<h2 id="3-安装"><a href="#3-安装" class="headerlink" title="3.安装"></a>3.安装</h2><p>注意版本Tornado和python的版本：</p>
<ul>
<li>Tornado4.3 可以运行在Python2.6、2.7、3.2+</li>
<li>Tornado6.0需要Python3.5.2+</li>
</ul>
<h3 id="3-1-python环境安装"><a href="#3-1-python环境安装" class="headerlink" title="3-1.python环境安装"></a>3-1.python环境安装</h3><p>python环境可以分为<strong>本地环境、虚拟环境、远程环境</strong></p>
<ul>
<li>本地环境<blockquote>
<p>本机安装个python，然后配置下环境变量。</p>
</blockquote>
</li>
<li>虚拟环境<blockquote>
<p>借用一些软件(比如：Anaconda、Virtualenv/Virtualenvwrapper)，安装虚拟环境，可以达到隔离效果，多个python在一台机器上也不会冲突</p>
</blockquote>
</li>
<li>远程环境<blockquote>
<p>非本机，一台远程机器上python环境</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>我使用的是Anaconda，如何使用和安装可以参照这篇blog。 <a href="https://iworkh.gitee.io/blog/2020/05/21/python_install/">python环境安装</a></p>
</blockquote>
<p>至于virtualenv和本机安装环境，自行用搜索引擎解决下</p>
<ul>
<li><a href="https://www.cnblogs.com/jiap/p/9855444.html" target="_blank" rel="noopener">python虚拟环境 Virtualenv及Virtualenvwrapper</a></li>
</ul>
<h3 id="3-2-开发工具"><a href="#3-2-开发工具" class="headerlink" title="3-2.开发工具"></a>3-2.开发工具</h3><p>主流的python开发工具</p>
<ul>
<li>pycharm</li>
<li>vscode</li>
<li>eclipse</li>
</ul>
<blockquote>
<p>我使用的是pycharm，如何安装也不多介绍了。里面有下载地址：<a href="https://iworkh.gitee.io/blog/2020/05/21/python_install/">python环境安装</a></p>
</blockquote>
<h3 id="3-3-pip使用"><a href="#3-3-pip使用" class="headerlink" title="3-3.pip使用"></a>3-3.pip使用</h3><p>pip如何使用，如何切换国内网，也都看这篇blog吧。<a href="https://iworkh.gitee.io/blog/2020/05/21/python_install/">python环境安装</a></p>
<h3 id="3-4-安装tornado"><a href="#3-4-安装tornado" class="headerlink" title="3-4.安装tornado"></a>3-4.安装tornado</h3><pre><code class="shell"># 使用anaconda创建一个tornado虚拟环境
conda create --name iworkh-tornado python=3.7

# 查看有哪些环境
conda info -e

# 切换到tornado虚拟环境
activate iworkh-tornado

# pip安装tornado
pip install tornado

# 或使用condo安装tornado
conda install tornado</code></pre>
<h3 id="3-5-创建项目"><a href="#3-5-创建项目" class="headerlink" title="3-5.创建项目"></a>3-5.创建项目</h3><p><strong>pycharm创建项目</strong></p>
<img src="/blog/images/python/tornado/tornado_pycharm_new_project.png" width="650px">

<p>因为前异步使用anaconda创建一个tornado虚拟环境，所以就使用已存在的环境了。</p>
<blockquote>
<p>当然如果前面没有自己创建虚拟环境，也可以使用pycharm里的<code>new environment using XXX</code>（选中虚拟化工具），来创建新的。</p>
</blockquote>
<p>至此，tornado的开发环境基本ok了，如果有问题，可在留言区留言，或者QQ联系我。</p>
<h2 id="4-tornado优势"><a href="#4-tornado优势" class="headerlink" title="4.tornado优势"></a>4.tornado优势</h2><h3 id="4-1-web框架比较"><a href="#4-1-web框架比较" class="headerlink" title="4-1.web框架比较"></a>4-1.web框架比较</h3><p>提到python web框架，那么就想到了Django和Flask，那么他们有啥区别呢？</p>
<ul>
<li><a href="https://www.jianshu.com/p/9960a9667a5c" target="_blank" rel="noopener">Django、Flask与Tornado的性能对比</a></li>
<li><a href="https://m.imooc.com/article/24759" target="_blank" rel="noopener">从django、flask、tornado的部署说起</a></li>
<li><a href="https://www.cnblogs.com/johnchow/p/4884555.html" target="_blank" rel="noopener">python框架(flask/django/tornado)比较</a><blockquote>
<p>可以读下这几篇blog</p>
</blockquote>
</li>
</ul>
<p><strong>总结以几点下：</strong></p>
<ul>
<li>重量级<blockquote>
<p>django比较重量级；flask和tornado都比较轻量级</p>
</blockquote>
</li>
<li>开发效率<blockquote>
<p>django大而全的框架,效率高；</p>
</blockquote>
</li>
<li>并发能力<blockquote>
<p>django和flask同步，能力查；tornado异步框架，性能相对好</p>
</blockquote>
</li>
<li>长连接<blockquote>
<p>tornado适合用于开发长连接多的web应用。比如股票信息推送、网络聊天等。</p>
</blockquote>
</li>
<li>数据库与模板处理性能<blockquote>
<p>Tornado与Flask旗鼓相当;django慢</p>
</blockquote>
</li>
<li>部署方面<blockquote>
<p>tornado即使web框架，也是web服务；flask和tornado知识web框架，需要借助web服务器来部署</p>
</blockquote>
</li>
</ul>
<h3 id="4-2-如何高并发"><a href="#4-2-如何高并发" class="headerlink" title="4-2.如何高并发"></a>4-2.如何高并发</h3><ul>
<li>异步非阻塞IO（基于epoll事件驱动）</li>
<li>通过协程来实现高并发处理</li>
</ul>
<h3 id="4-3-建议"><a href="#4-3-建议" class="headerlink" title="4-3.建议"></a>4-3.建议</h3><ul>
<li>不在tornado中使用大量的同步IO操作 (发挥不了tornado的特性)</li>
<li>不要将耗时的操作都往线程池中扔 （这并不能提高tornado多大的性能）</li>
<li>tornado的线程和协程是两个概念</li>
<li>编程时使用asnyc和await操作，而不建议coroutine协程装饰器和yield<blockquote>
<p>coroutine是一个gen方式历史过滤方案，在python3.x后，已经使用了asnyc</p>
</blockquote>
</li>
</ul>
<h2 id="5-tornado异步编程"><a href="#5-tornado异步编程" class="headerlink" title="5.tornado异步编程"></a>5.tornado异步编程</h2><p>我们都知道cpu的处理能力要远远大于IO的处理能力，而IO是阻塞操作，即io阻塞了，cpu即在休息，在虚度时间，在cpu资源，（浪费是可耻的，我们要利用最大化）。</p>
<p>当然不只本地磁盘IO，我们访问网络，常使用的<code>requests</code>和<code>urllib</code>的库都是同步的。</p>
<h3 id="5-1-io模型"><a href="#5-1-io模型" class="headerlink" title="5-1.io模型"></a>5-1.io模型</h3><p><a href="https://www.iworkh.com/book/dev/iworkh-dev-java/docs/#/zh-cn/8-7-IO-NIO-Info" target="_blank" rel="noopener">iworkh-java-nio的linux io分类</a></p>
<p>io模型主要分为<strong>同步/异步</strong>，<strong>阻塞/非阻塞</strong></p>
<blockquote>
<p>同步和异步：是相对于<strong>调用方</strong>而言的<br>阻塞和非阻塞： 是相对于<strong>被调用方</strong>而言的</p>
</blockquote>
<p>譬如：用户程序（用户空间，A）调用了文件系统内容（内核空间，B），假设B操作比较耗时要10秒钟。</p>
<blockquote>
<p>对于A而言，是立刻得到B的返回结果呢，还是一直等着。等B处理完能够通知A，还是A要一直在等着？（同步/异步）<br>对于B而言，接收到A的请求了，处理完后，如何能够通知到A，不用限制着A，要一直等着？（阻塞/非阻塞）</p>
</blockquote>
<p><strong>阻塞</strong></p>
<p>安装下requests工具表</p>
<pre><code class="shell">pip install requests</code></pre>
<p>代码</p>
<pre><code class="python">import requests
blogHtml = requests.get(&quot;https://iworkh.gitee.io/blog/&quot;).content.decode(&quot;utf8&quot;)
print(blogHtml)</code></pre>
<blockquote>
<p><code>requests.get</code>是阻塞，如果网不好或服务器响应很慢的话，那阻塞这回很耗时</p>
</blockquote>
<p><strong>非阻塞</strong></p>
<pre><code class="python">import socket

# AF_INET：服务器之间网络通信，SOCK_STREAM 流式socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置非阻塞
client.setblocking(False)

# 传个tuple
address = &quot;www.baidu.com&quot;
try:
    client.connect((&quot;www.baidu.com&quot;, 80))
except BlockingIOError as e:
    print(&quot;连接中...干些其他事&quot;)
    pass

while True:
    try:
        client.send(&quot;GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n&quot;.format(&quot;/&quot;, address).encode(&quot;utf8&quot;))
        print(&quot;连接成功&quot;)
        break
    except OSError as e:
        pass

# 定义bytes
data = b&quot;&quot;

while 1:
    try:
        rec = client.recv(512)
        if rec:
            data += rec
        else:
            print(data.decode(&quot;utf8&quot;))
            break
    except BlockingIOError as e:
        pass</code></pre>
<blockquote>
<p><code>setblocking</code>设置为非阻塞的，然后后面一直使用while循环来处理，直到正常返回结果。(比太关注死循环的写法，而要关注于<strong>非阻塞</strong>了)</p>
</blockquote>
<h3 id="5-2-io多路复用"><a href="#5-2-io多路复用" class="headerlink" title="5-2.io多路复用"></a>5-2.io多路复用</h3><p>IO多路复用：一个进程监控多个描述符fds，当描述符状态为read、write等状态时，通知程序进行相应的操作。</p>
<p>select、poll和epoll都是多路复用机制。它们本质上也是同步IO，它们在读写过程是阻塞的。</p>
<p>而异步IO是非阻塞，由内核将数据从内核空间拷贝到用户空间</p>
<ul>
<li><a href="https://blog.csdn.net/armlinuxww/article/details/92803381" target="_blank" rel="noopener">🐂Epoll原理解析</a><blockquote>
<p>像了解其原理德胡啊，强力建议去看篇blog</p>
</blockquote>
</li>
</ul>
<h4 id="5-2-1-select"><a href="#5-2-1-select" class="headerlink" title="5-2-1.select"></a>5-2-1.select</h4><p>select函数监控3类文件描述符: readfds,writefds,exceptfds。<br>window和linux都支持，但是单进监控的文件描述符有限，linux一般为1024。</p>
<img src="/blog/images/python/tornado/select_interrupt.png" width="650px">

<ul>
<li>没有数据时，进程A被放到等待队列，即cpu没有处理进程A</li>
</ul>
<img src="/blog/images/python/tornado/select_wake.png" width="650px">

<ul>
<li>当有数据来，socket会处于就绪状态，将进程A放到cpu队列中去工作</li>
<li>但是线程A不知道哪些socket是就绪的，所以只能全部遍历一边，去找到就状态了。</li>
</ul>
<h4 id="5-2-3-poll"><a href="#5-2-3-poll" class="headerlink" title="5-2-3.poll"></a>5-2-3.poll</h4><p>poll使用一个pollfd指针实现。pollfd监控even事件，虽们没有最大限制，但是太多也影响性能。需要遍历所有的描述符。</p>
<blockquote>
<p>某时刻，文件描述符，处于就绪状态很少。但是却要遍历所有描述符，因此当文件描述符很多时，性能会下降。</p>
</blockquote>
<h4 id="5-2-4-epoll"><a href="#5-2-4-epoll" class="headerlink" title="5-2-4.epoll"></a>5-2-4.epoll</h4><p>epoll对select和poll增强。是在2.6内核中提出的。没有最大文件描述符的限制。</p>
<p>epoll使用一个文件描述符区管理多个描述符，将用户的文件描述符event存在内核时间表中，这样用户空间和内核只要copy一次。</p>
<p>由于唤醒的进程，不知道哪些是就绪状态，所以可以将就绪状态的socket存下以便查找，不用都所有都遍历一遍。</p>
<img src="/blog/images/python/tornado/epoll_wait.png" width="650px">

<ul>
<li>使用了eventpoll来记录，监控进程和哪些socket是处于就绪状态</li>
</ul>
<img src="/blog/images/python/tornado/epoll_wake.png" width="650px">

<ul>
<li>当Socket接收到数据，中断程序一方面修改 Rdlist，另一方面唤醒 eventpoll 等待队列中的进程，进程A再次进入运行状态</li>
<li>也因为 Rdlist 的存在，进程 A 可以知道哪些 Socket 发生了变化。</li>
</ul>
<h4 id="5-2-5-回调异步"><a href="#5-2-5-回调异步" class="headerlink" title="5-2-5.回调异步"></a>5-2-5.回调异步</h4><p>上面异步代码，都是while和try来处理，这我们修改下使用select方式进行改造</p>
<pre><code class="python">import socket

from selectors import DefaultSelector, EVENT_WRITE, EVENT_READ

selector = DefaultSelector()

class CallBackRequestSite:
    def get_url(self, url):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.setblocking(False)
        self.host = url
        self.data = b&quot;&quot;
        try:
            self.client.connect((self.host, 80))
        except BlockingIOError as e:
            print(&quot;连接中...干些其他事&quot;)
            pass

        selector.register(self.client.fileno(), EVENT_WRITE, self.connected)

    def connected(self, key):
        selector.unregister(key.fd)
        self.client.send(&quot;GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n&quot;.format(&quot;/&quot;, self.host).encode(&quot;utf8&quot;))
        selector.register(self.client.fileno(), EVENT_READ, self.read_data)

    def read_data(self, key):
        recv_data = self.client.recv(1024)
        if recv_data:
            self.data += recv_data
        else:
            selector.unregister(key.fd)
            print(self.data.decode(&quot;utf8&quot;))

def loop():
    while 1:
        # 根据系统是否支持，使用epoll还是select，优先epoll。默认阻塞，有活动连接就返回活动的连接列表
        ready = selector.select()
        for key, mask in ready:
            callback = key.data
            callback(key)


if __name__ == &#39;__main__&#39;:
    demo = CallBackRequestSite()
    demo.get_url(&quot;www.baidu.com&quot;)
    loop()</code></pre>
<blockquote>
<p>上面方式是通过回调函数的方式实现的异步操作，虽然异步了，但是回调方式的缺点也很明显</p>
</blockquote>
<p>回调方式的缺点</p>
<ul>
<li>回调太多，会导致调用链太深（回调地狱）</li>
<li>不便于阅读和维护</li>
</ul>
<h3 id="5-3-协程"><a href="#5-3-协程" class="headerlink" title="5-3.协程"></a>5-3.协程</h3><p>协程（Coroutines）是一种比线程更加轻量级的存在，正如一个进程可以拥有多个线程一样，一个线程可以拥有多个协程。</p>
<p>进程和线程是由系统控制，而协程是由程序自己控制的。好处是性能大幅度的提升，因为不会像线程切换那样消耗资源。</p>
<p>一个进程可以包含多个线程，一个线程也可以包含多个协程。一个线程的多个协程的运行是串行的。当一个协程运行时，其它协程必须挂起。</p>
<blockquote>
<p>多核CPU，多个进程或一个进程内的多个线程是可以并行运行的，但一个线程内协程却绝对是串行的，无论CPU有多少个核。</p>
</blockquote>
<p>如对进程、线程、协程不太清除的阅读这篇文件 <strong><a href="https://www.jianshu.com/p/6dde7f92951e" target="_blank" rel="noopener">进程、线程、协程</a></strong></p>
<ul>
<li><a href="https://tornado-zh.readthedocs.io/zh/latest/guide/coroutines.html" target="_blank" rel="noopener">官网协程协程介绍</a></li>
</ul>
<img src="/blog/images/python/tornado/process_thread_coroutine.png" width="550px">

<h4 id="5-3-1-yield生成器"><a href="#5-3-1-yield生成器" class="headerlink" title="5-3-1.yield生成器"></a>5-3-1.yield生成器</h4><p>协程的一个重要基础就是：程序运行一个协程能够暂停，去运行另外的协程。（如何暂停？才是关键）</p>
<blockquote>
<p>yield就是可以满足程序（函数/方法）执行时，暂停。</p>
</blockquote>
<pre><code class="python">def yield_method():
    print(&quot;do start...&quot;)
    yield 1
    res2 = yield 2
    print(&quot;do c...res2:{}&quot;.format(res2))
    yield 3</code></pre>
<p>这是最简单的函数中使用yield的例子，<code>resp = yield_method()</code>是一个<code>generator</code>生成器。</p>
<ul>
<li><code>next(resp)</code>: 可以使得程序从一个yield到下一个yield的执行。（当到最后一个yield时，再执行next会报错）</li>
<li><code>send</code>: 可以发送参数给之前yield，并执行到下一个yield</li>
<li>foreach：一般都使用foreach来处理</li>
</ul>
<p>不懂的可以阅读下这篇文章 <a href="https://blog.csdn.net/mieleizhi0522/article/details/82142856/" target="_blank" rel="noopener">python中yield的用法详解</a></p>
<pre><code class="python">def yield_method():
    print(&quot;do start...&quot;)
    yield 1
    print(&quot;do a...&quot;)
    res1 = yield 2
    print(&quot;do b...res1: {}&quot;.format(res1))
    res2 = yield 3
    print(&quot;do c...res2:{}&quot;.format(res2))
    yield 4


def call_hand():
    resp = yield_method()
    print(next(resp))  # 运行到 yield 1处，返回 1
    print(resp.__next__())  # 从yield 1开始运行，到 yield 2处，返回 2
    print(resp.send(&quot;got it&quot;))  # 从yield 2开始运行，并把send值给yield 2的变量，到 yield 3处，返回 3
    print(next(resp))  # 从yield 3开始运行，到 yield 4处，返回 4

def call_for():
    for item in yield_method():
        print(item)


if __name__ == &#39;__main__&#39;:
    print(yield_method())
    print(&quot;*&quot; * 30)
    call_hand()
    print(&quot;*&quot; * 30)
    call_for()
</code></pre>
<h4 id="5-3-2-异步"><a href="#5-3-2-异步" class="headerlink" title="5-3-2.异步"></a>5-3-2.异步</h4><p>在tornado中，定义协程代码有多种方式</p>
<ul>
<li>@coroutine装饰器（装饰器又分多家的，tornado和python都有）(这不推荐使用了)</li>
<li>async方式 (推荐使用)</li>
</ul>
<p>直接上代码，里面有注释，具体自己悟（如有问题，评论区留言，或qq我。）</p>
<pre><code class="python">import asyncio
from asyncio import coroutine as pycoroutine
from time import sleep, time

from tornado.gen import coroutine as tcoroutine


async def async_method():
    await asyncio.sleep(2)
    print(&quot;do a...&quot;)
    await await_sleep_one_min()
    print(&quot;do b...python的async和await&quot;)
    await t_yield_sleep_one_min()
    print(&quot;do c...tornado装饰器&quot;)
    await py_yield_from_sleep_one_min()
    print(&quot;do d...python装饰器&quot;)
    await asyc_sleep_one_min()
    print(&quot;do e...&quot;)


# 不推荐使用, tornado装饰器方式
@tcoroutine
def t_yield_sleep_one_min():
    print(&quot;等了又等--1&quot;)
    yield sleep(1)
    print(&quot;等了又等--2&quot;)
    yield sleep(1)
    print(&quot;等了又等--3&quot;)
    yield sleep(1)


# 不推荐使用，python装饰器方式，python的旧语法
@pycoroutine
def py_yield_from_sleep_one_min():
    yield from asyncio.sleep(1)


# 推荐使用（async与await组合，因为asyncio.sleep也是个异步代码，需要用await）
async def await_sleep_one_min():
    await asyncio.sleep(1)


# 推荐使用（async单独使用，因为sleep不是个异步方法，不需要用await）
async def asyc_sleep_one_min():
    print(&#39;asyc_sleep_one_min----waiting&#39;)
    sleep(1)


if __name__ == &#39;__main__&#39;:
    start = time()
    asyncio.run(async_method())
    end = time()
    print(&#39;cost time = &#39; + str(end - start))
</code></pre>
<blockquote>
<p>简单总结下：</p>
<ul>
<li>协程定义可以用装饰器或者async修饰(推荐async)，后面只说async形式了。</li>
<li>协程A调用其他协程B(A和B函数都用async)，协程A里调用协程B的方法加使用<code>await</code>关键字，当然普通函数(非协程)不需要加await的</li>
</ul>
</blockquote>
<h4 id="5-3-3-执行"><a href="#5-3-3-执行" class="headerlink" title="5-3-3.执行"></a>5-3-3.执行</h4><p>执行协程方法，也有多种：tornado的IOLoop和python的asyncio</p>
<p><strong>python方式</strong></p>
<p>方式一：一直run</p>
<pre><code class="python">import asyncio

if __name__ == &#39;__main__&#39;:
    start = time()
    # 一直run
    asyncio.ensure_future(async_method())
    asyncio.get_event_loop().run_forever()

    end = time()
    print(&#39;cost time = &#39; + str(end - start))</code></pre>
<p>方式二：run完就结束</p>
<pre><code class="python">asyncio.run(async_method())</code></pre>
<p>方式三：run完就结束</p>
<pre><code class="python">asyncio.get_event_loop().run_until_complete(async_method())</code></pre>
<p><strong>tornado方式</strong></p>
<pre><code class="python"># 通过tornado的IOLoop来run (执行某个协程后停止事件循环)
IOLoop.current().run_sync(async_method)</code></pre>
<h3 id="5-4-AsnycHttpClient"><a href="#5-4-AsnycHttpClient" class="headerlink" title="5-4.AsnycHttpClient"></a>5-4.AsnycHttpClient</h3><p><strong>同步方式HTTPClient</strong></p>
<pre><code class="python">from tornado import httpclient

def get_url(url):
    http_client = httpclient.HTTPClient()
    resp = http_client.fetch(url)
    print(resp.body.decode(&quot;utf8&quot;))

if __name__ == &#39;__main__&#39;:
    web_site = &quot;https://iworkh.gitee.io/blog/&quot;
    get_url(web_site)
</code></pre>
<p><strong>异步方式AsnycHttpClient</strong></p>
<pre><code class="python">import asyncio

from tornado import httpclient


async def async_get_url(url):
    http_client = httpclient.AsyncHTTPClient()
    resp = await http_client.fetch(url)
    print(resp.body.decode(&quot;utf8&quot;))


if __name__ == &#39;__main__&#39;:
    web_site = &quot;https://iworkh.gitee.io/blog/&quot;
    asyncio.run(async_get_url(web_site))</code></pre>
<h2 id="6-tornado-web基础"><a href="#6-tornado-web基础" class="headerlink" title="6.tornado web基础"></a>6.tornado web基础</h2><p><code>tornado.web</code>提供了一种带有异步功能并允许它扩展到大量开放连接的简单的web框架, 使其成为处理长连接(long polling) 的一种理想选择.</p>
<h3 id="6-1-简单示例"><a href="#6-1-简单示例" class="headerlink" title="6-1.简单示例"></a>6-1.简单示例</h3><pre><code class="python">import time

from tornado import web
from tornado.ioloop import IOLoop


class HelloWorldHandlerOne(web.RequestHandler):
    async def get(self, *args, **kwargs):
        # 别在handler中写同步的代码,会被阻塞的
        time.sleep(5)
        self.write(&quot;hello world---one&quot;)


class HelloWorldHandlerTwo(web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write(&quot;hello world---two&quot;)


url_map = [
    (&quot;/test1&quot;, HelloWorldHandlerOne),
    (&quot;/test2&quot;, HelloWorldHandlerTwo)
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_map, debug=True)
    app.listen(8888)
    IOLoop.current().start()
</code></pre>
<p><strong>几点说明</strong></p>
<ul>
<li>handler类要继承<code>web.RequestHandler</code>，然后重写rest一些接口（get,post,put,delete等）</li>
<li>启动类，使用了<code>web.Application</code>，传了参数url和handler对应关系。debug参数</li>
<li>在handler的方法里不要调用同步的方法，这样会阻塞请求。<blockquote>
<p>即便请了不同的接口（test1和test2），由于test1的handler中<code>time.sleep(5)</code>，先请求test1，后请求test2，test2也会等test1响应完，才会响应test2（单线程的）。</p>
</blockquote>
</li>
</ul>
<p><strong>debug为true</strong></p>
<ul>
<li>debug设置为true，修改了代码，不用重启服务</li>
<li>idea的<code>Run</code>启动：后台启动。如果关闭需要控制面板后台关闭</li>
<li>idea的<code>Debug</code>启动：修改内容，会自动停了。得再次debug启动</li>
<li>命令行启动：后台启动。<code>ctr+c</code>可以退出</li>
</ul>
<p><strong>测试url</strong></p>
<ul>
<li><a href="http://localhost:8888/test1" target="_blank" rel="noopener">http://localhost:8888/test1</a></li>
<li><a href="http://localhost:8888/test2" target="_blank" rel="noopener">http://localhost:8888/test2</a></li>
</ul>
<h3 id="6-2-url匹配"><a href="#6-2-url匹配" class="headerlink" title="6-2.url匹配"></a>6-2.url匹配</h3><p>url匹配主要是对于python的表达式的使用,如不了解可以先去了解下 <a href="https://www.runoob.com/python3/python3-reg-expressions.html" target="_blank" rel="noopener">菜鸟教程-python-正则表达式</a></p>
<pre><code class="python">from tornado import web, ioloop


class IndexHandler(web.RequestHandler):
    async def get(self, *args, **kwargs):
        self.write(&quot;首页&quot;)


class UserHandler(web.RequestHandler):
    async def get(self, name, *args, **kwargs):
        self.write(&quot;hello world, {}&quot;.format(name))


class ProductHandler(web.RequestHandler):
    def get(self, name, count, *args, **kwargs):
        self.write(&quot;购买{}个{}&quot;.format(count, name))


class DivHandler(web.RequestHandler):
    #  one,two这个要跟url里的(?P&lt;one&gt;\d+)/(?P&lt;two&gt;\d+)里的名称匹配
    def get(self, one, two, *args, **kwargs):
        try:
            result = int(one) / int(two)
            self.write(&quot;{}/{}={}&quot;.format(one, two, result))
        except ZeroDivisionError:
            # 发生错误，会跳转
            self.redirect(self.reverse_url(&#39;error_page&#39;, 500))
            pass


class ErrorHandler(web.RequestHandler):
    def get(self, code, *args, **kwargs):
        self.write(&quot;发生错误：error_code: {}&quot;.format(code))


url_map = [
    (&quot;/user/(\w+)/?&quot;, UserHandler),
    (&quot;/product/(\w+)/(\d+)/?&quot;, ProductHandler),
    (&quot;/calc/div/(?P&lt;one&gt;\d+)/(?P&lt;two&gt;\d+)/?&quot;, DivHandler),
    web.URLSpec(&quot;/index/?&quot;, IndexHandler, name=&quot;index&quot;),
    web.URLSpec(&quot;/error/(?P&lt;code&gt;\d+)/?&quot;, ErrorHandler, name=&quot;error_page&quot;)
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_map, debug=True)
    app.listen(8888)
    print(&#39;started...&#39;)
    ioloop.IOLoop.current().start()</code></pre>
<p><strong>几点说明</strong></p>
<ul>
<li><code>/?</code>表示0或1个<code>/</code>,即url最后可以有<code>/</code>也可以没有<code>/</code></li>
<li>url定义，可以使用tuple简单实现，也可以使用<code>web.URLSpec</code>来创建对象，可以指定一些参数(url,handler,kwargs,name)</li>
<li><code>(?P&lt;xxx&gt;\d+)</code>,表示取一个组名，这个组名必须是唯一的，不重复的，没有特殊符号,然后跟参数里名称要一样</li>
<li><code>redirect</code>重定向,<code>reverse_url</code>根据名称类获取url</li>
</ul>
<p><strong>测试url</strong></p>
<ul>
<li><a href="http://localhost:8888/index/" target="_blank" rel="noopener">http://localhost:8888/index/</a><br>结果:<blockquote>
<p>首页</p>
</blockquote>
</li>
<li><a href="http://localhost:8888/user/iworkh" target="_blank" rel="noopener">http://localhost:8888/user/iworkh</a><br>结果:<blockquote>
<p>hello world, iworkh</p>
</blockquote>
</li>
<li><a href="http://localhost:8888/product/apple/3" target="_blank" rel="noopener">http://localhost:8888/product/apple/3</a><br>结果:<blockquote>
<p>购买3个apple</p>
</blockquote>
</li>
<li><a href="http://localhost:8888/calc/div/4/2" target="_blank" rel="noopener">http://localhost:8888/calc/div/4/2</a><br>结果:<blockquote>
<p>4/2=2.0</p>
</blockquote>
</li>
<li><a href="http://localhost:8888/calc/div/4/0" target="_blank" rel="noopener">http://localhost:8888/calc/div/4/0</a><br>结果:<blockquote>
<p>发生错误：error_code: 500</p>
</blockquote>
</li>
<li><a href="http://localhost:8888/errpr/404" target="_blank" rel="noopener">http://localhost:8888/errpr/404</a><br>结果:<blockquote>
<p>发生错误：error_code: 404</p>
</blockquote>
</li>
</ul>
<h3 id="6-3-动态传参"><a href="#6-3-动态传参" class="headerlink" title="6-3.动态传参"></a>6-3.动态传参</h3><p>动态传参：将一些配置参数通过命令行、配置文件方式动态传给程序，而不是代码写死。增加灵活性。</p>
<p>官网关于options的介绍 <a href="https://www.tornadoweb.org/en/stable/options.html" target="_blank" rel="noopener">options-doc</a></p>
<p>动态传参主要使用的<code>tornado.options</code>某块,使用步骤:</p>
<ul>
<li>先定义哪些参数<code>options.define()</code></li>
<li>从命令行或文件获取参数值<code>options.parse_command_line()</code>,<code>options.parse_config_file(&quot;/server.conf&quot;)</code></li>
<li>使用参数<code>options.xxx</code></li>
</ul>
<h4 id="6-3-1-命令方式"><a href="#6-3-1-命令方式" class="headerlink" title="6-3-1.命令方式"></a>6-3-1.命令方式</h4><p><strong>直接上代码</strong></p>
<pre><code class="python">from tornado import options, web, ioloop

options.define(&quot;port&quot;, default=&quot;8888&quot;, type=int, help=&quot;端口号&quot;)
options.define(&quot;static_path&quot;, default=&quot;static&quot;, type=str, help=&quot;静态资源路径&quot;)


class IndexHandler(web.RequestHandler):
    async def get(self):
        await self.finish(&#39;welcome to iworkh !!!&#39;)

url_map = [
    (&quot;/?&quot;, IndexHandler)
]

setting = {
    &#39;debug&#39;: True
}

if __name__ == &#39;__main__&#39;:
    # 命令行方式
    options.parse_command_line()
    app = web.Application(url_map, **setting)
    app.listen(options.options.port)
    ioloop.IOLoop.current().start()
</code></pre>
<blockquote>
<p>这注意: url_map是个list,而setting是个dic,去<code>web.Application</code>的初始化方法中就可以看到需要的参数类型</p>
</blockquote>
<p>启动命令</p>
<pre><code class="shell">python option_demo.py --port=8080</code></pre>
<blockquote>
<p>通过<code>--port</code>指定启动端口</p>
</blockquote>
<p>测试url</p>
<ul>
<li><a href="http://localhost:8080/" target="_blank" rel="noopener">http://localhost:8080/</a></li>
</ul>
<h4 id="6-3-2-文件方式"><a href="#6-3-2-文件方式" class="headerlink" title="6-3-2.文件方式"></a>6-3-2.文件方式</h4><p>将上面启动main代码修改为以下:</p>
<pre><code class="python">if __name__ == &#39;__main__&#39;:
    # 配置文件方式
    options.parse_config_file(&#39;config.ini&#39;)
    app = web.Application(url_map, **setting)
    app.listen(options.options.port)
    ioloop.IOLoop.current().start()</code></pre>
<p><strong>配置文件内容</strong></p>
<pre><code class="ini">port = 8889</code></pre>
<blockquote>
<p>就加了端口号</p>
</blockquote>
<p>测试url</p>
<ul>
<li><a href="http://localhost:8889/" target="_blank" rel="noopener">http://localhost:8889/</a></li>
</ul>
<h3 id="6-4-handler"><a href="#6-4-handler" class="headerlink" title="6-4.handler"></a>6-4.handler</h3><p><strong>因为后面内容,请求的方式不仅仅是get,还有post等,所以使用<code>Postman</code>工具来请求</strong></p>
<blockquote>
<p>Postman很简单,自行下载使用下,这就不多介绍了</p>
</blockquote>
<p>handler里内容和细节相当得多，具体可以参照官网 <a href="https://www.tornadoweb.org/en/stable/web.html" target="_blank" rel="noopener">RequestHandler-doc</a></p>
<p>我们主要从以下几个方面入手</p>
<ul>
<li>input (前台传来的参数，如何解析.)</li>
<li>process (RequestHandler常用的方法get,post,put,delete)</li>
<li>output (后台处理后，如何返回给前台)</li>
<li>常用的handler(RequestHandler,ErrorHandler,FallbackHandler,RedirectHandler,StaticFileHandler)</li>
<li>还有cookie和application,这部分看下官网</li>
</ul>
<h4 id="6-4-1-input"><a href="#6-4-1-input" class="headerlink" title="6-4-1.input"></a>6-4-1.input</h4><p><strong>常用的函数</strong></p>
<pre><code class="txt">`get_argument`,`get_arguments`: 从post的form中解析参数,如果form中没有,那会从url中解析
`get_query_argument`,`get_query_arguments`: 从url中解析参数
`get_body_argument`,`get_body_arguments`: json格式数据解析
`request`
`path_args`
`path_kwargs`</code></pre>
<blockquote>
<p>注意函数名有s和没有s的区别:</p>
<ul>
<li>有s表示接受的是一个数组,即使一个数据,也会转为数组形式.</li>
<li>没有s的,但是传了多个值(比如:<a href="http://localshot:8888/user?name=lilei&amp;&amp;name=wangwu),最后一个会生效(name=wangwu)" target="_blank" rel="noopener">http://localshot:8888/user?name=lilei&amp;&amp;name=wangwu),最后一个会生效(name=wangwu)</a></li>
</ul>
</blockquote>
<p><strong>结论:</strong></p>
<table>
<thead>
<tr>
<th>方法</th>
<th>传参</th>
</tr>
</thead>
<tbody><tr>
<td>get_query_argument(s)</td>
<td>参数在url中</td>
</tr>
<tr>
<td>get_argument(s)</td>
<td>参数在form中,没有头信息,form中没有还可以从url中获取</td>
</tr>
<tr>
<td>get_body_argument(s)</td>
<td>参数在form中,带有头信息</td>
</tr>
<tr>
<td>request.body</td>
<td>参数是json</td>
</tr>
</tbody></table>
<p><strong>演示代码</strong></p>
<pre><code class="python">import json

from tornado import web, ioloop


class UrlParamHandler(web.RequestHandler):
    async def get(self):
        name = self.get_query_argument(&quot;name&quot;)
        age = self.get_query_argument(&quot;age&quot;)
        self.write(&quot;name: {}, age: {}&quot;.format(name, age))

        self.write(&#39;&lt;br/&gt;&#39;)
        names = self.get_query_arguments(&quot;name&quot;)
        ages = self.get_query_arguments(&quot;age&quot;)
        self.write(&quot;names: {}, ages: {}&quot;.format(names, ages))


class FormParamHandler(web.RequestHandler):
    async def post(self):
        name = self.get_argument(&quot;name&quot;)
        age = self.get_argument(&quot;age&quot;)
        self.write(&quot;name: {}, age: {}&quot;.format(name, age))

        self.write(&#39;&lt;br/&gt;&#39;)
        names = self.get_arguments(&quot;name&quot;)
        ages = self.get_arguments(&quot;age&quot;)
        self.write(&quot;names: {}, ages: {}&quot;.format(names, ages))


class JsonWithFormHeadersParamHandler(web.RequestHandler):
    async def post(self):
        name = self.get_body_argument(&quot;name&quot;)
        age = self.get_body_argument(&quot;age&quot;)
        self.write(&quot;name: {}, age: {}&quot;.format(name, age))


class JsonParamHandler(web.RequestHandler):
    async def post(self):
        parm = json.loads(self.request.body.decode(&quot;utf8&quot;))
        name = parm[&quot;name&quot;]
        age = parm[&quot;age&quot;]
        self.write(&quot;name: {}, age: {}&quot;.format(name, age))


url_handers = [
    (&quot;/url&quot;, UrlParamHandler),
    (&quot;/form&quot;, FormParamHandler),
    (&quot;/jsonwithformheaders&quot;, JsonWithFormHeadersParamHandler),
    (&quot;/json&quot;, JsonParamHandler)
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handers, debug=True)
    app.listen(8888)
    print(&quot;started...&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<p><strong>测试url</strong><br>测试工具Postman,当然也可以使用python的<code>requests</code>来请求</p>
<p><strong>1. 参数在url中,get请求</strong></p>
<p><strong>代码请求接口方式测试</strong></p>
<pre><code class="python">import requests
url=&quot;http://localhost:8888/url?name=zhangsan&amp;&amp;age=20&quot;
resp = requests.request(&#39;GET&#39;, url).content.decode(&quot;utf8&quot;)
print(resp)</code></pre>
<p><strong>postman或浏览器方式测试</strong></p>
<ul>
<li><p><a href="http://localhost:8888/url?name=zhangsan&amp;&amp;age=20" target="_blank" rel="noopener">http://localhost:8888/url?name=zhangsan&amp;&amp;age=20</a><br>结果:</p>
<blockquote>
<p>name: zhangsan, age: 20<br>names: [‘zhangsan’], ages: [‘20’]</p>
</blockquote>
</li>
<li><p><a href="http://localhost:8888/url?name=zhangsan&amp;&amp;age=20&amp;&amp;name=lisi&amp;&amp;age=28" target="_blank" rel="noopener">http://localhost:8888/url?name=zhangsan&amp;&amp;age=20&amp;&amp;name=lisi&amp;&amp;age=28</a><br>结果:</p>
<blockquote>
<p>name: lisi, age: 28<br>names: [‘zhangsan’, ‘lisi’], ages: [‘20’, ‘28’]</p>
</blockquote>
</li>
</ul>
<p>由上面结果,可以看出函数名有s和没s的区别了,下面就其他方法就演示了,因为有s的,就不演示了跟这类似.</p>
<blockquote>
<p>个人觉得,不用s,传多个值,为啥不直接传数组了.特别后面是json格式交互的时候,多个值的场景完全可以数组作为值来传递</p>
</blockquote>
<p><strong>2. 参数在form中,没有请求头,post请求</strong></p>
<p><strong>代码请求接口方式测试</strong></p>
<pre><code class="python">import requests
url = &quot;http://localhost:8888/form&quot;
data = {
    &quot;name&quot;: &quot;zhangsan&quot;,
    &quot;age&quot;: 20
}
resp = requests.request(&#39;POST&#39;, url, data=data).content.decode(&quot;utf8&quot;)</code></pre>
<p><strong>postman方式测试</strong></p>
<ul>
<li><a href="http://localhost:8888/form" target="_blank" rel="noopener">http://localhost:8888/form</a><br>结果:<blockquote>
<p>name: lisi, age: 28<br>names: [‘zhangsan’, ‘lisi’], ages: [‘20’, ‘28’]</p>
<img src="/blog/images/python/tornado/handler_input_arguments.png" width="650px">
</blockquote>
</li>
</ul>
<p><strong>3. 参数在from中,并含有请求头<code>Content-Type:application/x-www-form-urlencoded</code>,post请求</strong><br><strong>代码请求接口方式测试</strong></p>
<pre><code class="python">import requests
url = &quot;http://localhost:8888/jsonwithformheaders&quot;
headers = {&quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded;&quot;}
data = {
    &quot;name&quot;: &quot;zhangsan&quot;,
    &quot;age&quot;: 20
}
resp = requests.request(&#39;POST&#39;, url, headers=headers, data=data).content.decode(&quot;utf8&quot;)
print(resp)</code></pre>
<p><strong>postman方式测试</strong></p>
<ul>
<li><a href="http://localhost:8888/jsonwithform" target="_blank" rel="noopener">http://localhost:8888/jsonwithform</a><img src="/blog/images/python/tornado/handler_input_body_argument.png" width="650px">

</li>
</ul>
<p><strong>4. 参数是json,没有头,post请求</strong></p>
<p><strong>代码请求接口方式测试</strong></p>
<pre><code class="python">import requests
url = &quot;http://localhost:8888/json&quot;
data = {
    &quot;name&quot;: &quot;zhangsan&quot;,
    &quot;age&quot;: 20
}
resp = requests.request(&#39;POST&#39;, url, json=data).content.decode(&quot;utf8&quot;)
print(resp)</code></pre>
<blockquote>
<p>注意这这参数传的是<code>json=data</code>,而不是<code>data=data</code>哦</p>
</blockquote>
<p><strong>postman方式测试</strong></p>
<ul>
<li><a href="http://localhost:8888/json" target="_blank" rel="noopener">http://localhost:8888/json</a><blockquote>
<p>没有头,只能通过<code>request.body</code>来拿到参数,而且是bytes类型,需要decode后,json解析后得到dict</p>
<img src="/blog/images/python/tornado/handler_input_request_body.png" width="650px">
</blockquote>
</li>
</ul>
<p><strong>提示</strong>: 注意json值传递时候,有没header,取值方式不同.结论如下:</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>传参</th>
</tr>
</thead>
<tbody><tr>
<td>get_query_argument(s)</td>
<td>参数在url中</td>
</tr>
<tr>
<td>get_argument(s)</td>
<td>参数在form中,没有头信息</td>
</tr>
<tr>
<td>get_body_argument(s)</td>
<td>参数在form中,带有头信息</td>
</tr>
<tr>
<td>request.body</td>
<td>参数是json</td>
</tr>
</tbody></table>
<h4 id="6-4-2-process"><a href="#6-4-2-process" class="headerlink" title="6-4-2.process"></a>6-4-2.process</h4><p>注意: process里函数的执行顺序</p>
<pre><code class="txt">initialize: 初始化,通过`URLSpec`可将参数传递到方法
prepare: 在get/post等操作之前,会调用
get/head/post/delete/patch/put/options: 一般业务逻辑处理
on_finish: 在out操作之后,会调用</code></pre>
<p><strong>直接上代码</strong></p>
<pre><code class="python">from tornado import web, ioloop


class ProcessDemoHandler(web.RequestHandler):

    # initialize方法同步
    def initialize(self, dbinfo):
        print(&quot;initialize...&quot;)
        self.dbinfo = dbinfo
        print(&quot;数据库host:{}&quot;.format(self.dbinfo[&#39;db_host&#39;]))
        pass

    async def prepare(self):
        print(&quot;prepare...&quot;)
        pass

    async def get(self):
        print(&quot;get...&quot;)
        self.write(&quot;success!!!&quot;)
        pass

    def on_finish(self):
        print(&quot;finish...&quot;)
        pass


init_param = {
    &#39;dbinfo&#39;: {
        &#39;db_host&#39;: &#39;localhost&#39;,
        &#39;db_port&#39;: 3306,
        &#39;db_user&#39;: &#39;root&#39;,
        &#39;db_password&#39;: &#39;123&#39;,
    }
}

url_handers = [
    (r&quot;/?&quot;, ProcessDemoHandler, init_param),
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handers, debug=True)
    app.listen(8888)
    print(&quot;started...&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<p>需要关注以下几点:</p>
<blockquote>
<p>1.顺序: initialize 👉 prepare 👉 get/post/put/delete/… 👉 on_finish<br>2.initialize和on_finish方法同步<br>3.如何将参数通过<code>url_handers</code>传给initialize方法</p>
</blockquote>
<p>结果:</p>
<pre><code class="txt">initialize...
数据库host:localhost
prepare...
get...</code></pre>
<h4 id="6-4-3-output"><a href="#6-4-3-output" class="headerlink" title="6-4-3.output"></a>6-4-3.output</h4><pre><code class="txt">set_status: 设置状态码
set_header
add_header
clear_header
write: 写数据,可以write多次,放缓存中,而不会中断当flush或者finish或者没消息断开时发送
flush: 刷新数据到客户端
finish: 写数据,写完断开了
render/render_string: 模板方式渲染输出
redirect: 重定向
send_error/write_error: 发送错误
render_embed_js/render_embed_css: 嵌入的js和css
render_linked_js/render_linked_css: 链接的js和css</code></pre>
<pre><code class="python">import asyncio
from tornado import web, ioloop

class OutputDemoHandler(web.RequestHandler):
    async def get(self):
        self.set_status(200)
        self.write(&quot;error!!!&quot;)
        self.write(&quot;warning!!!&quot;)
        self.write(&quot;&lt;br/&gt;&quot;)
        await self.flush()
        await asyncio.sleep(5)
        self.write(&quot;success!!!&quot;)
        await self.finish(&quot;done&quot;)


url_handers = [
    (r&quot;/?&quot;, OutputDemoHandler),
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handers, debug=True)
    app.listen(8888)
    print(&quot;started...&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<blockquote>
<p>这里主要使用了<code>write</code>,<code>flush</code>以及<code>finish</code>操作,其他前面用过就不多介绍了.<br>没用过的<code>render_xxx</code>形式的函数,在模板中使用的。</p>
</blockquote>
<p><strong>结果:</strong></p>
<pre><code class="txt">error!!!warning!!!
success!!!done</code></pre>
<blockquote>
<p>第一行先出来,应为flush了,过了5秒后,第二行数据出来</p>
</blockquote>
<h3 id="6-5-setting"><a href="#6-5-setting" class="headerlink" title="6-5.setting"></a>6-5.setting</h3><p>setting里每项值类型都是<code>dic</code>类型</p>
<p>配置主要分以下几种类型</p>
<ul>
<li>General settings</li>
<li>Authentication and security settings:</li>
<li>Template settings</li>
<li>Static file settings</li>
</ul>
<p>这不会全部介绍，只介绍下常用的，在实际开发中，去官网查阅.  </p>
<p>官网setting配置项 <a href="https://www.tornadoweb.org/en/stable/web.html#" target="_blank" rel="noopener">setting配置</a></p>
<blockquote>
<p>打开后，直接搜索关键字<code>settings</code>，就能定位到  </p>
</blockquote>
<p><strong>常用配置</strong></p>
<p>debug</p>
<blockquote>
<p>是否开启debug模式</p>
</blockquote>
<p>default_handler_class </p>
<blockquote>
<p>当url没有配置到handler时,默认处理器.</p>
</blockquote>
<p>static_path</p>
<blockquote>
<p>静态资源路径</p>
</blockquote>
<p>static_url_prefix</p>
<blockquote>
<p>静态资源前缀</p>
</blockquote>
<p>static_handler_class</p>
<blockquote>
<p>静态资源handler(默认是<code>tornado.web.StaticFileHandler</code>)</p>
</blockquote>
<p><strong>静态资源代码演示</strong></p>
<pre><code class="python">from tornado import web, ioloop

class SettingDemoHandler(web.RequestHandler):
    async def get(self):
        await self.finish(&quot;success&quot;)


url_handers = [
    (r&quot;/?&quot;, SettingDemoHandler),
]

settings={
    &#39;debug&#39;: True,
    &#39;static_path&#39;: &#39;public/static&#39;,
    &#39;static_url_prefix&#39;: &#39;/static/&#39;
}

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handers, **settings)
    app.listen(8888)
    print(&quot;started...&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<blockquote>
<p>在相对路径下,创建<code>public/static/images</code>文件夹,并放一张图片进去</p>
</blockquote>
<p>测试url</p>
<ul>
<li><a href="http://localhost:8888/static/images/moon.jpg" target="_blank" rel="noopener">http://localhost:8888/static/images/moon.jpg</a><blockquote>
<p>端口号后面的前缀url紧跟的路径，要跟<code>static_url_prefix</code>配置一致</p>
</blockquote>
</li>
</ul>
<h3 id="6-6-模板"><a href="#6-6-模板" class="headerlink" title="6-6.模板"></a>6-6.模板</h3><p>模板就不多介绍了，工作中我们一般开发都前后端分离，因此这就不过多纠缠了。(单为了文章的完整性，我还是写了个简单例子)</p>
<blockquote>
<p>前端：react、vue等前端框架<br>后端：java、go、python等语言编写的web框架跟前端进行数据交互</p>
</blockquote>
<p>有兴趣的可以看下官网：<a href="https://www.tornadoweb.org/en/stable/template.html" target="_blank" rel="noopener">模板-docs</a></p>
<p><strong>主要包含以下几点</strong></p>
<pre><code class="python">1.普通字符串模板，加载使用
2.文件模板，加载使用
3.常用的模板语法 {{ data }},{% set ...%},{% raw %}
4.导入python，调用函数，循环输出
5.模板继承 {% extends "xxx.html" %}
6.UIModule使用 {% module Template("foo.html", arg=42) %}</code></pre>
<h4 id="6-6-1-页面结构"><a href="#6-6-1-页面结构" class="headerlink" title="6-6-1.页面结构"></a>6-6-1.页面结构</h4><p><strong>基本结构</strong></p>
<img src="/blog/images/python/tornado/tornado_tp_arc.png" width="650px">

<h4 id="6-6-1-结果"><a href="#6-6-1-结果" class="headerlink" title="6-6-1.结果"></a>6-6-1.结果</h4><p><strong>结果</strong></p>
<img src="/blog/images/python/tornado/tornado_tp_result.png" width="650px">

<h4 id="6-6-2-代码"><a href="#6-6-2-代码" class="headerlink" title="6-6-2.代码"></a>6-6-2.代码</h4><p>代码主要分模板、UIMoudle、orderHandler、启动类</p>
<p><strong>启动类和handler</strong></p>
<pre><code class="python">from typing import Optional
from tornado import web, ioloop

class OrderHandler(web.RequestHandler):

    def get(self):
        data = {
            &#39;title&#39;: &#39;书本列表&#39;,
            &#39;orderList&#39;: [
                {&#39;id&#39;: 1, &#39;name&#39;: &#39;java&#39;, &#39;price&#39;: 80, &#39;count&#39;: 1,
                 &#39;link_to&#39;: &#39;&lt;a href=&quot;http://www.baidu.com&quot;&gt;查看&lt;/a&gt;&#39;},
                {&#39;id&#39;: 2, &#39;name&#39;: &#39;springboot&#39;, &#39;price&#39;: 100, &#39;count&#39;: 2,
                 &#39;link_to&#39;: &#39;&lt;a href=&quot;http://www.baidu.com&quot;&gt;查看&lt;/a&gt;&#39;},
                {&#39;id&#39;: 3, &#39;name&#39;: &#39;springcloud&#39;, &#39;price&#39;: 120, &#39;count&#39;: 1,
                 &#39;link_to&#39;: &#39;&lt;a href=&quot;http://www.baidu.com&quot;&gt;查看&lt;/a&gt;&#39;},
            ]
        }
        self.render(&quot;tp_order.html&quot;, **data)


class FooterUIMoudle(web.UIModule):

    def render(self):
        return self.render_string(&quot;uimodules/tp_footer.html&quot;)

    def embedded_css(self) -&gt; Optional[str]:
        css = &#39;&#39;&#39;
        .content { background: gray}
        a {text-decoration-line: none}
        &#39;&#39;&#39;
        return css


url_handlers = [
    (r&quot;/order&quot;, OrderHandler)
]

settings = {
    &quot;debug&quot;: True,
    &quot;template_path&quot;: &#39;public/templates&#39;,
    &quot;static_path&quot;: &#39;public/static&#39;,
    &quot;static_url_prefix&quot;: &quot;/static/&quot;,
    &quot;ui_modules&quot;: {
        &#39;FooterUIMoudle&#39;: FooterUIMoudle
    }
}

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handlers, **settings)
    app.listen(8888)
    print(&quot;started successfully&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<blockquote>
<p>settings里定义<code>ui_modules</code>，<code>template_path</code>,<code>static_path</code><br>注意给模板传参数的类型，在目录中也需要跟其对于方式取出值</p>
</blockquote>
<p><strong>tp_base.html模板</strong><br>相对启动类路径：<code>public/templates/tp_base.html</code></p>
<pre><code class="tmplate">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;商城&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{{ static_url('css/base.css') }}&quot;&gt;
    {% block custom_css %}
    {% end %}
&lt;/head&gt;
&lt;body&gt;

    &lt;div class=&quot;header&quot;&gt;
        {% module Template("uimodules/tp_header.html") %}
    &lt;/div&gt;
    &lt;div class=&quot;content&quot;&gt;
        {% block order_html_block%}
            &lt;h2&gt;订单&lt;/h2&gt;
        {% end %}
    &lt;/div&gt;


    {% module FooterUIMoudle() %}


    {% block custom_js %}
    {% end %}
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<blockquote>
<p>里面使用UIMoudle、继承等手段</p>
</blockquote>
<p><strong>tp_base.html模板</strong><br>相对启动类路径：<code>public/templates/tp_base.html</code></p>
<pre><code class="tmplate">{% extends 'tp_base.html' %}

{% block order_html_block %}
&lt;div class=&quot;center&quot;&gt;
    &lt;table style=&quot;margin: 0 auto&quot;&gt;
        &lt;caption&gt;{{ title }}&lt;/caption&gt;
        &lt;thead&gt;
        &lt;tr&gt;
            &lt;td&gt;id&lt;/td&gt;
            &lt;td&gt;名称&lt;/td&gt;
            &lt;td&gt;价格&lt;/td&gt;
            &lt;td&gt;数量&lt;/td&gt;
            &lt;td&gt;详情&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        {% set total_price = 0 %}
        {% for item in orderList %}
        &lt;tr&gt;
            &lt;td&gt;{{ item["id"] }}&lt;/td&gt;
            &lt;td&gt;{{ item["name"] }}&lt;/td&gt;
            &lt;td&gt;{{ item["price"] }}元&lt;/td&gt;
            &lt;td&gt;{{ item["count"] }}&lt;/td&gt;
            &lt;td&gt;{% raw item["link_to"] %}&lt;/td&gt;
            &lt;div style=&quot;display: none&quot;&gt;{{ total_price = total_price + item["price"] * item["count"] }}&lt;/div&gt;
        &lt;/tr&gt;
        {% end %}
        &lt;/tbody&gt;
    &lt;/table&gt;

    总价格： {{ total_price }}
&lt;/div&gt;
{% end %}</code></pre>
<blockquote>
<p>里面使用继承、循环、raw、赋值等手段</p>
</blockquote>
<p><strong>uimoudles下的tp_footer.html模板</strong><br>相对启动类路径：<code>public/templates/uimodules/tp_header.html</code></p>
<pre><code class="tmplate">&lt;h2&gt;welcome to iworkh !!!&lt;/h2&gt;</code></pre>
<p><strong>uimoudles下的tp_footer.html模板</strong><br>相对启动类路径：<code>public/templates/uimodules/tp_footer.html</code></p>
<pre><code class="tmplate">&lt;div class=&quot;footer&quot;&gt;
    &lt;div style=&quot;padding: 0px 4px 10px; text-align: center; font-size: 12px;&quot;&gt;&lt;span&gt;©2019&amp;nbsp;iworkh&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://beian.miit.gov.cn/&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;margin-left: 10px; color: rgb(147, 147, 147);&quot;&gt;苏ICP备19061993号&lt;/span&gt;&lt;/a&gt;&lt;a target=&quot;_blank&quot; href=&quot;http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=32010602010683&quot;&gt;&lt;span style=&quot;margin-left: 10px; color: rgb(147, 147, 147);&quot;&gt;苏公网安备 32010602010683号&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<h2 id="7-mysql"><a href="#7-mysql" class="headerlink" title="7.mysql"></a>7.mysql</h2><p>python里PyMySQL是同步，所以在tornado我们得用异步的，这介绍下aiomysql，它是基于PyMySQL的。用法差不错，只不过实现了异步操作。</p>
<ul>
<li><a href="https://aiomysql.readthedocs.io/en/latest/index.html" target="_blank" rel="noopener">aimysql-doc</a></li>
</ul>
<p><strong>安装依赖</strong></p>
<pre><code class="shell">pip install aiomysql</code></pre>
<h3 id="7-1-简单示例"><a href="#7-1-简单示例" class="headerlink" title="7-1.简单示例"></a>7-1.简单示例</h3><pre><code class="python">import asyncio
import aiomysql

async def test_example(loop):
    pool = await aiomysql.create_pool(host=&#39;127.0.0.1&#39;, port=3306,
                                      user=&#39;root&#39;, password=&#39;&#39;,
                                      db=&#39;mysql&#39;, loop=loop)
    async with pool.acquire() as conn:
        async with conn.cursor() as cur:
            await cur.execute(&quot;SELECT 42;&quot;)
            print(cur.description)
            (r,) = await cur.fetchone()
            assert r == 42
    pool.close()
    await pool.wait_closed()


loop = asyncio.get_event_loop()
loop.run_until_complete(test_example(loop))</code></pre>
<p>这是官网Github上给的示例，主要注意以下几点</p>
<blockquote>
<ul>
<li>async异步编写</li>
<li>调用异步操作时，要使用await</li>
<li>async with的用法，异步上下文管理器，接受会将对应的资源关闭的（异步上下文管理器指的是在enter和exit方法处能够暂停执行的上下文管理器，<code>__aenter__</code> 和<code>__aexit__</code>方法。）</li>
</ul>
</blockquote>
<p>有兴趣可以去了解下<code>async with</code>和<code>async for</code>，百度下随便挑一篇阅读下就明白了（基本都雷同的😊）</p>
<p><strong>创建表</strong></p>
<pre><code class="sql">CREATE TABLE `tb_order` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `count` int(5) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;</code></pre>
<p><strong>插入一条数据</strong></p>
<pre><code class="sql">INSERT INTO `iworkh_tornado`.`tb_order`(`id`, `name`, `price`, `count`)
VALUES (1, &#39;java&#39;, 80.00, 1);</code></pre>
<h3 id="7-1-查询数据"><a href="#7-1-查询数据" class="headerlink" title="7-1.查询数据"></a>7-1.查询数据</h3><p>使用get请求来请求rest接口，参数方法可以放在url中，也可以方法在form中 (通过get_argument获取参数)</p>
<p><strong>查询逻辑</strong></p>
<pre><code class="python">import aiomysql
from tornado import web, ioloop

class OrderHandler(web.RequestHandler):
    def initialize(self, db_info) -&gt; None:
        self.db_info = db_info

    async def get(self):
        id = self.get_argument(&quot;id&quot;, None)
        if not id:
            raise Exception(&quot;id can not None&quot;)
        db_id = db_name = db_price = db_count = None
        async with aiomysql.create_pool(host=self.db_info[&quot;db_host&quot;], port=self.db_info[&quot;db_port&quot;],
                                        user=self.db_info[&quot;db_user&quot;], password=self.db_info[&quot;db_password&quot;],
                                        db=self.db_info[&quot;db_name&quot;], charset=self.db_info[&quot;db_charset&quot;]) as pool:
            async with pool.acquire() as conn:
                async with conn.cursor() as cur:
                    sql = &quot;SELECT `id`, `name`, `price`, `count` FROM TB_ORDER WHERE id = {};&quot;.format(id)
                    print(sql)
                    await cur.execute(sql)
                    result = await cur.fetchone()
                    try:
                        if (result):
                            db_id, db_name, db_price, db_count = result
                    except Exception:
                        pass

            self.write(&quot;success, {}, {}, {}, {}&quot;.format(db_id, db_name, db_price, db_count))

initParam = {
    &quot;db_info&quot;: {
        &#39;db_host&#39;: &#39;localhost&#39;,
        &#39;db_name&#39;: &#39;iworkh_tornado&#39;,
        &#39;db_port&#39;: 3306,
        &#39;db_user&#39;: &#39;iworkh&#39;,
        &#39;db_password&#39;: &#39;iworkh123&#39;,
        &#39;db_charset&#39;: &#39;utf8&#39;
    }
}

url_handlers = [
    (r&quot;/order/?&quot;, OrderHandler, initParam)
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handlers, debug=True)
    app.listen(8888)
    print(&quot;started successfully&quot;)
    ioloop.IOLoop.current().start()</code></pre>
<p><strong>测试代码</strong></p>
<pre><code class="python">import requests

url = &quot;http://localhost:8888/order&quot;
data={
    &#39;id&#39;:&#39;1&#39;
}
resp = requests.get(url, data=data).content.decode(&quot;utf8&quot;)
print(resp)</code></pre>
<h3 id="7-2-插入数据"><a href="#7-2-插入数据" class="headerlink" title="7-2.插入数据"></a>7-2.插入数据</h3><p><strong>查询逻辑</strong></p>
<pre><code class="python">import aiomysql
from tornado import web, ioloop


class OrderHandler(web.RequestHandler):
    def initialize(self, db_info) -&gt; None:
        self.db_info = db_info

    async def post(self):
        db_name= self.get_argument(&quot;name&quot;)
        db_price= self.get_argument(&quot;price&quot;)
        db_count= self.get_argument(&quot;count&quot;)

        async with aiomysql.create_pool(host=self.db_info[&quot;db_host&quot;], port=self.db_info[&quot;db_port&quot;],
                                        user=self.db_info[&quot;db_user&quot;], password=self.db_info[&quot;db_password&quot;],
                                        db=self.db_info[&quot;db_name&quot;], charset=self.db_info[&quot;db_charset&quot;]) as pool:

            async with pool.acquire() as conn:
                async with conn.cursor() as cur:
                    sql = &quot;INSERT INTO TB_ORDER (`name`, `price`, `count`) VALUES (&#39;{}&#39;,&#39;{}&#39;,&#39;{}&#39;);&quot;\
                        .format(db_name, db_price, db_count)
                    print(sql)
                    await cur.execute(sql)
                    await conn.commit()
            self.write(&quot;save successfully&quot;)

initParam = {
    &quot;db_info&quot;: {
        &#39;db_host&#39;: &#39;localhost&#39;,
        &#39;db_name&#39;: &#39;iworkh_tornado&#39;,
        &#39;db_port&#39;: 3306,
        &#39;db_user&#39;: &#39;iworkh&#39;,
        &#39;db_password&#39;: &#39;iworkh123&#39;,
        &#39;db_charset&#39;: &#39;utf8&#39;
    }
}

url_handlers = [
    (r&quot;/order/?&quot;, OrderHandler, initParam)
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handlers, debug=True)
    app.listen(8888)
    print(&quot;started successfully&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<p><strong>测试代码</strong></p>
<pre><code class="python">import requests

url = &quot;http://localhost:8888/order&quot;
data = {
    &#39;name&#39;:&#39;springboot&#39;,
    &#39;price&#39;: 100.0,
    &#39;count&#39;:2
}
resp = requests.post(url, data=data).content.decode(&quot;utf8&quot;)
print(resp)</code></pre>
<h3 id="7-3-更新数据"><a href="#7-3-更新数据" class="headerlink" title="7-3.更新数据"></a>7-3.更新数据</h3><p><strong>查询逻辑</strong></p>
<pre><code class="python">import aiomysql
from tornado import web, ioloop

class OrderHandler(web.RequestHandler):
    def initialize(self, db_info) -&gt; None:
        self.db_info = db_info

    async def put(self):
        db_id= self.get_argument(&quot;id&quot;)
        db_name= self.get_argument(&quot;name&quot;)
        db_price= self.get_argument(&quot;price&quot;)
        db_count= self.get_argument(&quot;count&quot;)

        async with aiomysql.create_pool(host=self.db_info[&quot;db_host&quot;], port=self.db_info[&quot;db_port&quot;],
                                        user=self.db_info[&quot;db_user&quot;], password=self.db_info[&quot;db_password&quot;],
                                        db=self.db_info[&quot;db_name&quot;], charset=self.db_info[&quot;db_charset&quot;]) as pool:


            async with pool.acquire() as conn:
                async with conn.cursor() as cur:
                    sql = &quot;UPDATE TB_ORDER SET `name` = &#39;{}&#39;, `price` = &#39;{}&#39;, `count` = &#39;{}&#39; WHERE `id` = {};&quot;\
                        .format(db_name, db_price, db_count, db_id)                   
                    print(sql)
                    await cur.execute(sql)
                    await conn.commit()
            self.write(&quot;save successfully&quot;)

initParam = {
    &quot;db_info&quot;: {
        &#39;db_host&#39;: &#39;localhost&#39;,
        &#39;db_name&#39;: &#39;iworkh_tornado&#39;,
        &#39;db_port&#39;: 3306,
        &#39;db_user&#39;: &#39;iworkh&#39;,
        &#39;db_password&#39;: &#39;iworkh123&#39;,
        &#39;db_charset&#39;: &#39;utf8&#39;
    }
}

url_handlers = [
    (r&quot;/order/?&quot;, OrderHandler, initParam)
]

if __name__ == &#39;__main__&#39;:
    app = web.Application(url_handlers, debug=True)
    app.listen(8888)
    print(&quot;started successfully&quot;)
    ioloop.IOLoop.current().start()
</code></pre>
<p><strong>测试代码</strong></p>
<pre><code class="python">import requests
url = &quot;http://localhost:8888/order&quot;
data = {
    &#39;id&#39;: 1,
    &#39;name&#39;:&#39;java&#39;,
    &#39;price&#39;: 60.0,
    &#39;count&#39;:1
}
resp = requests.put(url, data=data).content.decode(&quot;utf8&quot;)
print(resp)</code></pre>
<p>通过上面的aiomysl操作，虽然实现了异步操作，但是我们可以发现一些问题</p>
<ul>
<li>线程池操作，每次一个请求来都创建了个线程池，然后关闭</li>
<li>原生sql操作，拼sql很累</li>
<li>查询结果，解析很麻烦</li>
<li>model对象和数据库表关联操作，进行查询、更新、插入操作（有hibernate、jpa、Django orm等开发经验着，应该明白ORM框架带来的好处）</li>
</ul>
<h2 id="8-orm框架peewee"><a href="#8-orm框架peewee" class="headerlink" title="8.orm框架peewee"></a>8.orm框架peewee</h2><h3 id="8-1-orm好处"><a href="#8-1-orm好处" class="headerlink" title="8-1.orm好处"></a>8-1.orm好处</h3><p>我们通知之前对aiomysql对数据库的操作，我们发现了一些问题，操作很麻烦。</p>
<p>那使用orm有啥好处呢？</p>
<ul>
<li>操作更加方便，不用跟原生sql交互太多，便于维护。(特别写sql少了<code>,</code>,<code>&#39;</code>等一些低价错误，很难发现，浪费开发时间)</li>
<li>orm可以屏蔽调底层数据（不管mysql、postgresql、sqlite等，都通过orm来操作，来适配）</li>
<li>还能防止sql注入</li>
</ul>
<p>python领域常用的一些ORM框架</p>
<ul>
<li>Django’s ORM</li>
<li>peewee</li>
<li>SQLAlchemy</li>
</ul>
<p><strong>Django’s ORM</strong></p>
<pre><code class="txt">优点：
    易用，学习曲线短
    和Django紧密集合，用Django时使用约定俗成的方法去操作数据库

缺点：
    QuerySet速度不给力，会逼我用Mysqldb来操作原生sql语句。
    不好处理复杂的查询，强制开发者回到原生SQL </code></pre>
<p><strong>peewee</strong></p>
<pre><code class="txt">优点：
    Django式的API，使其易用
    轻量实现，很容易和任意web框架集成
    aysnc-peewee异步操作

缺点：
    不支持自动化schema迁移
    不能像Django那样，使线上的mysql表结构生成结构化的模型。
    多对多查询写起来不直观 </code></pre>
<p><strong>SQLAlchemy</strong></p>
<pre><code class="txt">优点：
    巨牛逼的企业级API，使得代码有健壮性和适应性
    灵活的设计，使得能轻松写复杂查询

缺点：
    工作单元概念不常见
    重量级 API，导致长学习曲线</code></pre>
<h3 id="8-2-peewee同步操作"><a href="#8-2-peewee同步操作" class="headerlink" title="8-2.peewee同步操作"></a>8-2.peewee同步操作</h3><p>数据操作，主要就增删改查功能，下面分别演示如何使用。<a href="http://docs.peewee-orm.com/en/latest/index.html" target="_blank" rel="noopener">peewee官网doc</a></p>
<blockquote>
<p>工作中使用前，最好把官网浏览一遍，掌握一些常用的api和细节</p>
</blockquote>
<p>先pip安装</p>
<pre><code class="shell">pip install peewee</code></pre>
<h4 id="8-2-1-定义model"><a href="#8-2-1-定义model" class="headerlink" title="8-2-1.定义model"></a>8-2-1.定义model</h4><p><strong>base_model.py</strong></p>
<pre><code class="python">import datetime

from peewee import MySQLDatabase, Model, DateTimeField

db_info = {&#39;host&#39;: &#39;127.0.0.1&#39;, &#39;user&#39;: &#39;iworkh&#39;, &#39;password&#39;: &#39;iworkh123&#39;, &#39;charset&#39;: &#39;utf8&#39;, &#39;port&#39;: 3306}

db = MySQLDatabase(&#39;iworkh_tornado&#39;, **db_info)

class BaseModel(Model):
    created_date = DateTimeField(default=datetime.datetime.now)

    class Meta:
        database = db
</code></pre>
<p><strong>user_model.py</strong></p>
<pre><code class="python">from peewee import CharField, DateField, IntegerField, BooleanField

from lesson05.models.base_model import *

class User(BaseModel):
    username = CharField(column_name=&#39;username&#39;, unique=True, null=False, max_length=50, verbose_name=&quot;姓名&quot;)
    birthday = DateField(column_name=&#39;birthday&#39;, null=True, default=None, verbose_name=&#39;生日&#39;)
    phone = CharField(column_name=&#39;phone&#39;, max_length=11)
    mail = CharField(column_name=&#39;mail&#39;, max_length=50, verbose_name=&#39;邮件&#39;)
    gender = IntegerField(column_name=&#39;gender&#39;, null=False, default=1, verbose_name=&#39;姓别&#39;)
    is_admin = BooleanField(column_name=&#39;is_admin&#39;, default=False, verbose_name=&#39;是否是管理员&#39;)

    class Meta:
        table_name = &quot;T_USER&quot;</code></pre>
<pre><code class="python">from peewee import ForeignKeyField, CharField, IntegerField

from lesson05.models.base_model import BaseModel
from lesson05.models.user_model import User

class Pet(BaseModel):
    user = ForeignKeyField(User, backref=&#39;pets&#39;)
    name = CharField(index=True, column_name=&#39;name&#39;, max_length=50, verbose_name=&#39;名字&#39;)
    age = IntegerField(column_name=&#39;age&#39;, verbose_name=&#39;年龄&#39;)
    type = CharField(column_name=&#39;type&#39;, max_length=50, verbose_name=&#39;类型&#39;)
    color = CharField(column_name=&#39;color&#39;, max_length=50, verbose_name=&#39;颜色&#39;)
    description = CharField(column_name=&#39;description&#39;, max_length=500, verbose_name=&#39;描述&#39;)

    class Meta:
        table_name = &quot;T_PET&quot;</code></pre>
<h4 id="8-2-2-初始化表"><a href="#8-2-2-初始化表" class="headerlink" title="8-2-2.初始化表"></a>8-2-2.初始化表</h4><pre><code class="python">import 省略

if __name__ == &#39;__main__&#39;:
    db.connect()
    table_list = [User, Pet]
    db.create_tables(table_list)
    print(&quot;end...&quot;)</code></pre>
<h4 id="8-2-3-插入"><a href="#8-2-3-插入" class="headerlink" title="8-2-3.插入"></a>8-2-3.插入</h4><p>插入数据，可以使用<code>save</code>和<code>create</code>方法来操作</p>
<blockquote>
<ul>
<li>save: 对象操作</li>
<li>create: 是类操作，看create函数上有<code>@classmethod</code>修饰符</li>
</ul>
</blockquote>
<pre><code class="python">import datetime
import 省略

userList = [
    {&#39;username&#39;: &#39;zhangsan&#39;, &#39;birthday&#39;: &#39;1988-08-08&#39;, &#39;gender&#39;: 1, &#39;mail&#39;: &#39;zhangsan@qq.com&#39;, &#39;phone&#39;: &#39;111&#39;,
     &#39;is_admin&#39;: False},
    {&#39;username&#39;: &#39;lisi&#39;, &#39;birthday&#39;: datetime.date(1999, 8, 8), &#39;gender&#39;: 1, &#39;mail&#39;: &#39;lisi@qq.com&#39;, &#39;phone&#39;: &#39;222&#39;,
     &#39;is_admin&#39;: False},
    {&#39;username&#39;: &#39;wangwu&#39;, &#39;birthday&#39;: datetime.date(2025, 8, 8), &#39;gender&#39;: 0, &#39;mail&#39;: &#39;wangwu@qq.com&#39;, &#39;phone&#39;: &#39;333&#39;,
     &#39;is_admin&#39;: False},
    {&#39;username&#39;: &#39;admin&#39;, &#39;birthday&#39;: datetime.date(2000, 8, 8), &#39;gender&#39;: 1, &#39;mail&#39;: &#39;admin@qq.com&#39;, &#39;phone&#39;: &#39;444&#39;,
     &#39;is_admin&#39;: True}
]


def save():
    user = User()
    user.username = &#39;iworkh_save&#39;
    user.birthday = datetime.date(1988, 8, 8)
    user.gender = 0
    user.mail = &quot;157162006@qq.com&quot;
    user.phone = &quot;888888888&quot;
    user.is_admin = True

    # 使用对象调用方法
    row = user.save()
    print(&quot;save的返回值是值:{}&quot;.format(row))


def save_list():
    for item in userList:
        user = User(**item)
        user.save()


def create():
    user_dic = {
        &#39;username&#39;: &#39;iworkh_create2&#39;,
        &#39;birthday&#39;: datetime.date(1989, 8, 8),
        &#39;gender&#39;: 1,
        &#39;mail&#39;: &#39;157162006@qq.com&#39;,
        &#39;phone&#39;: &#39;888888888&#39;,
        &#39;is_admin&#39;: False
    }

    # 使用类调用方法
    row = User.create(**user_dic)
    print(&quot;create的返回值值:{}&quot;.format(row))


if __name__ == &#39;__main__&#39;:
    save()
    create()
    save_list()
    print(&quot;end...&quot;)
</code></pre>
<h4 id="8-2-4-查询"><a href="#8-2-4-查询" class="headerlink" title="8-2-4.查询"></a>8-2-4.查询</h4><p>查询比较重要，但是peewee的api使用，跟原生sql语法优点类似</p>
<blockquote>
<p>sql: select * from user where username like %iworkh%<br>peewee写法：User.select().where(User.username.contains(‘iworkh’))</p>
</blockquote>
<pre><code class="python">import 省略

def select_all():
    # modelSelect = User.select() # sql: select * from user
    fields = [User.username, User.phone]
    modelSelect = User.select(*fields)  # sql: select username, phone from user
    # 这还没有立即执行，返回的是modelSelect对象，当for或者execute时才会执行
    for user in modelSelect:
        print(&quot;{} --- {} --- {}&quot;.format(user.username, user.phone, user.is_admin))


def select_conditon():
    # 根据id取记录，如果id不存在，那么转化时候会报错
    user_res_get_by_id = User.get_by_id(1)
    print(&quot;get_by_id 结果：{}&quot;.format(user_res_get_by_id.username))

    user_res_get = User.get(User.id == 1)
    print(&quot;user_res_get 结果：{}&quot;.format(user_res_get.username))

    user_res_dic = User[1]
    print(&quot;user_res_dic 结果：{}&quot;.format(user_res_dic.username))

    print(&quot;*&quot; * 50)

    # select * from user where username like %iworkh%
    modelSelect = User.select().where(User.username.contains(&#39;iworkh&#39;))
    for user in modelSelect:
        print(&quot;{} --- {}&quot;.format(user.username, user.is_admin))

    print(&quot;*&quot; * 50)

    # select * from user ordery by birthday desc
    modelSelect_order = User.select().order_by(User.birthday.desc())
    for user in modelSelect_order:
        print(&quot;{} --- {}&quot;.format(user.username, user.birthday))

    print(&quot;*&quot; * 50)
    # 按每页3的大小分页，取第2页数据(从1开始计数)
    modelSelect_pagable = User.select().paginate(page=2, paginate_by=3)
    for user in modelSelect_pagable:
        print(&quot;{} --- {}&quot;.format(user.username, user.id))


if __name__ == &#39;__main__&#39;:
    select_all()
    select_conditon()
</code></pre>
<h4 id="8-2-5-更新"><a href="#8-2-5-更新" class="headerlink" title="8-2-5.更新"></a>8-2-5.更新</h4><p>更新很简单，使用的是<code>save</code>函数，当save里传id值时，则认为是更新；id没有值None时，则是插入操作</p>
<pre><code class="python">import 省略

def upate():
    # 使用save方法即可更新，只要传的值中还有id
    user_res_get_by_id = User.get_by_id(1)
    user_res_get_by_id.username = user_res_get_by_id.username + &#39;_update&#39;
    # 从数据中取处记录，并修改值后更新数据库
    user_res_get_by_id.save()

if __name__ == &#39;__main__&#39;:
   upate()</code></pre>
<h4 id="8-2-6-删除"><a href="#8-2-6-删除" class="headerlink" title="8-2-6.删除"></a>8-2-6.删除</h4><p>删除记录有两种操作：</p>
<ul>
<li>类操作，根据id删除</li>
<li>对象操作，这个对象要id值，底层也是根据id来删除记录的</li>
</ul>
<pre><code class="python">import 省略

def delete_by_id():
    # 类操作
    User.delete_by_id(3)


def delete_instance():
    user = User()
    user.id = 4
    # 对象操作，recursive参数控制关联数据是否删除
    user.delete_instance()


if __name__ == &#39;__main__&#39;:
    delete_by_id()
    delete_instance()
    print(&quot;end...&quot;)</code></pre>
<h4 id="8-2-7-技巧"><a href="#8-2-7-技巧" class="headerlink" title="8-2-7.技巧"></a>8-2-7.技巧</h4><div class="admonition note">
<p class="fa admonition-title">技巧</p>
<p>
   peewee里还有很其他知识点，除了阅读官网提供的doc文档外，还有一些地方值得我们开发者注意的，就是源码下面的test和example模块。
</p>
</div>

<ul>
<li><a href="http://docs.peewee-orm.com/en/latest/peewee/query_examples.html" target="_blank" rel="noopener">doc-examples</a></li>
</ul>
<p>留个问题：</p>
<blockquote>
<p>peewee里transaction如何使用？(如何去官网doc和源码下快速找到你需要的答案呢？)</p>
</blockquote>
<h3 id="8-3-peewee-async操作"><a href="#8-3-peewee-async操作" class="headerlink" title="8-3.peewee-async操作"></a>8-3.peewee-async操作</h3><p>tornado调用接口，那得用异步的，所以peewee的同步操作，在tornado中肯定玩不转的。<a href="https://peewee-async.readthedocs.io/en/latest/" target="_blank" rel="noopener">peewee-async官网doc</a></p>
<p>peewee-async的接口主要分两部分</p>
<ul>
<li>高级API (推荐使用)<blockquote>
<p>主要通过manager来操作</p>
</blockquote>
</li>
<li>低级API (不推荐使用)<blockquote>
<p>主要通过peewee_async来操作</p>
</blockquote>
</li>
</ul>
<p><strong>安装</strong></p>
<pre><code class="shell">pip install --pre peewee-async</code></pre>
<h4 id="8-3-1-定义models"><a href="#8-3-1-定义models" class="headerlink" title="8-3-1.定义models"></a>8-3-1.定义models</h4><p>定义model还是跟peewee一样，不过db的方式使用了<code>peewee_async.MySQLDatabase</code></p>
<pre><code class="python">import datetime

import peewee_async
from peewee import Model, DateTimeField, TextField, CharField

db_info = {&#39;host&#39;: &#39;127.0.0.1&#39;, &#39;user&#39;: &#39;iworkh&#39;, &#39;password&#39;: &#39;iworkh123&#39;, &#39;charset&#39;: &#39;utf8&#39;, &#39;port&#39;: 3306}

db = peewee_async.MySQLDatabase(&#39;iworkh_tornado&#39;, **db_info)


class BaseModel(Model):
    created_date = DateTimeField(default=datetime.datetime.now)

    class Meta:
        database = db


class BlogModel(BaseModel):
    title = CharField(column_name=&#39;title&#39;, max_length=50)
    content = TextField(column_name=&#39;content&#39;)

    class Meta:
        table_name = &#39;t_blog&#39;</code></pre>
<h4 id="8-3-2-基本操作"><a href="#8-3-2-基本操作" class="headerlink" title="8-3-2.基本操作"></a>8-3-2.基本操作</h4><p>通过<strong>高级API</strong>的方式，进行增删改查操作</p>
<pre><code class="python">import asyncio

import peewee_async

import 省略了models的引入

objects = peewee_async.Manager(db)

async def save():
    blog = {&#39;title&#39;: &#39;tornado入门&#39;, &#39;content&#39;: &#39;详细内容请看文章&#39;}
    await objects.create(BlogModel, **blog)
    print(&quot;保存成功&quot;)


async def get_all():
    all_blogs = await objects.execute(BlogModel.select())
    for blog in all_blogs:
        print(&quot;id: {}, 文章标题：{}&quot;.format(blog.id, blog.title))


async def delete():
    blog = BlogModel()
    blog.id = 1
    await objects.delete(blog)
    print(&quot;删除成功&quot;)

def create_table():
    BlogModel.create_table(True)

def drop_table():
    BlogModel.drop_table(True)


async def test():
    create_table()
    await save()
    print(&quot;*&quot;*50)
    await get_all()
    print(&quot;*&quot;*50)
    await delete()
    print(&quot;*&quot;*50)
    await get_all()
    drop_table()


if __name__ == &#39;__main__&#39;:
    loop = asyncio.get_event_loop()
    loop.run_until_complete(test())
    print(&quot;end&quot;)
</code></pre>
<h2 id="9-参考"><a href="#9-参考" class="headerlink" title="9.参考"></a>9.参考</h2><ul>
<li><a href="https://www.jianshu.com/p/3a928ade93dc" target="_blank" rel="noopener">Tornado</a></li>
<li><a href="https://www.cnblogs.com/skiler/p/6867179.html" target="_blank" rel="noopener">Tornado之架构概述图</a></li>
<li><a href="https://www.jianshu.com/p/9960a9667a5c" target="_blank" rel="noopener">Django、Flask与Tornado的性能对比</a></li>
<li><a href="https://m.imooc.com/article/24759" target="_blank" rel="noopener">从django、flask、tornado的部署说起</a></li>
<li><a href="https://www.cnblogs.com/johnchow/p/4884555.html" target="_blank" rel="noopener">python框架(flask/django/tornado)比较</a></li>
<li><a href="https://blog.csdn.net/rebelqsp/article/details/22109925" target="_blank" rel="noopener">python socket编程详细介绍</a></li>
<li><a href="https://blog.csdn.net/armlinuxww/article/details/92803381" target="_blank" rel="noopener">🐂Epoll原理解析</a></li>
<li><a href="https://www.jianshu.com/p/6dde7f92951e" target="_blank" rel="noopener">进程、线程、协程</a></li>
<li><a href="https://blog.csdn.net/mieleizhi0522/article/details/82142856/" target="_blank" rel="noopener">python中yield的用法详解</a></li>
<li><a href="https://aiomysql.readthedocs.io/en/latest/index.html" target="_blank" rel="noopener">aimysql-doc</a></li>
<li><a href="https://www.cnblogs.com/suoning/p/5721225.html" target="_blank" rel="noopener">tornado-Cookie-CSRF-模板示例</a></li>
</ul>

      
       <hr><span style="font-style: italic;color: gray;"> 转载请注明来源，欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论，也可以邮件至 157162006@qq.com </span>
    </div>
</article>


<p>
    <a  class="dashang" onclick="dashangToggle()">赏</a>
</p>


<div class="article_copyright">
    <p><span class="copy-title">文章标题:</span>👍Tornado入门这一篇足以</p>
    <p><span class="copy-title">字数:</span><span class="post-count">11.7k</span></p>
    <p><span class="copy-title">本文作者:</span><a  title="沐雨云楼">沐雨云楼</a></p>
    <p><span class="copy-title">发布时间:</span>2020-06-08, 20:31:32</p>
    <p><span class="copy-title">最后更新:</span>2020-09-12, 21:21:47</p>
    <span class="copy-title">原始链接:</span><a class="post-url" href="/blog/2020/06/08/python_tornado_info/" title="👍Tornado入门这一篇足以">https://iworkh.gitee.io/blog/2020/06/08/python_tornado_info/</a>
    <p>
        <span class="copy-title">版权声明:</span><i class="fa fa-creative-commons"></i> <a rel="license noopener" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" title="CC BY-NC-SA 4.0 International" target = "_blank">&#34;署名-非商用-相同方式共享 4.0&#34;</a> 转载请保留原文链接及作者。
    </p>
</div>



    <div id="comments"></div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">

<script type="text/javascript">
    $.getScript('/blog/js/gitalk.js', function () {
        var gitalk = new Gitalk({
            clientID: 'e4a269e1ae1600031361',
            clientSecret: '6f3f981cbc49dd802fde779697d8f01da3a77981',
            repo: 'iworkh.github.io',
            owner: 'iworkh',
            admin: ['iworkh'],
            id: decodeURI(location.pathname),
            distractionFreeMode: 'true',
            language: 'zh-CN',
            perPage: parseInt('10',10)
        })
        gitalk.render('comments')
    })
</script>




    




    </div>
    <div class="copyright">
        <p class="footer-entry">©2020 iworkh</p>
<!--<p class="footer-entry">Built with <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/yelog/hexo-theme-3-hexo" target="_blank">3-hexo</a> theme</p>-->

    </div>
    <div class="full-toc">
        <button class="full" data-title="切换全屏 快捷键 s"><span class="min "></span></button>
<button class="post-toc-menu" data-title="打开TOC 快捷键 w"><span class="post-toc-menu-icons"></span></button>
<div class="post-toc"><span class="post-toc-title">目录</span>
    <div class="post-toc-content">

    </div>
</div>
<a class="" id="rocket" ></a>

    </div>
</div>
<div class="acParent"></div>

<div class="hide_box" onclick="dashangToggle()"></div>
<div class="shang_box">
    <a class="shang_close"  onclick="dashangToggle()">×</a>
    <div class="shang_tit">
        <p>喜欢就点赞,疼爱就打赏</p>
    </div>
    <div class="shang_payimg">
        <div class="pay_img">
            <img src="/blog/img/alipay.jpg" class="alipay" title="扫码支持">
            <img src="/blog/img/weixin.jpg" class="weixin" title="扫码支持">
        </div>
    </div>
    <div class="shang_payselect">
        <span><label><input type="radio" name="pay" checked value="alipay">支付宝</label></span><span><label><input type="radio" name="pay" value="weixin">微信</label></span>
    </div>
</div>


</body>
<script src="/blog/js/jquery.pjax.js?v=1.0.1" ></script>

<script src="/blog/js/script.js?v=1.0.1" ></script>
<script>
    var img_resize = 'default';
    /*作者、标签的自动补全*/
    $(function () {
        $('.search').AutoComplete({
            'data': ['@沐雨云楼','@iworkh','#ai','#attr','#aysnc','#beanmap','#bigdecimal','#blog','#chrome','#dict','#dll','#docker','#domain','#fastjson','#func','#git','#github','#hadoop','#hexo','#idea','#iworkh','#jackson','#java','#jdni','#jfinalshell','#joplin','#JPA','#lock','#markdowm','#maven','#mock','#mq','#mycat','#mysql','#nginx','#notebook','#plan','#powermock','#python','#redis','#rest','#sharding-jdbc','#spark','#springboot','#test','#tool','#tools','#tornado','#transactional','#web','#windows','#wtforms','#zookeeper','#分库分表','#分页','#命令','#工具','#并发','#序列化','#微服务','#性能分析','#手册','#有道云','#标签','#模板','#百度云盘','#结构和算法','#计划','#迁移','#面试',],
            'itemHeight': 20,
            'width': 418
        }).AutoComplete('show');
    })
    function initArticle() {
        /*渲染对应的表格样式*/
        
            $(".post .pjax table").addClass("green_title");
        

        /*渲染打赏样式*/
        
        $("input[name=pay]").on("click", function () {
            if($("input[name=pay]:checked").val()=="weixin"){
                $(".shang_box .shang_payimg .pay_img").addClass("weixin_img");
            } else {
                $(".shang_box .shang_payimg .pay_img").removeClass("weixin_img");
            }
        })
        

        /*高亮代码块行号*/
        
        $('pre code').each(function(){
            var lines = $(this).text().trim().split('\n').length, widther='';
            if (lines>99) {
                widther = 'widther'
            }
            var $numbering = $('<ul/>').addClass('pre-numbering ' + widther).attr("unselectable","on");
            $(this).addClass('has-numbering ' + widther)
                    .parent()
                    .append($numbering);
            for(var i=1;i<=lines;i++){
                $numbering.append($('<li/>').text(i));
            }
        });
        

        /*访问数量*/
        
        $.getScript("//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js");
        

        /*代码高亮，行号对齐*/
        $('.pre-numbering').css('line-height',$('.has-numbering').css('line-height'));

        
        
    }

    /*打赏页面隐藏与展示*/
    
    function dashangToggle() {
        $(".shang_box").fadeToggle();
        $(".hide_box").fadeToggle();
    }
    

</script>

<!--加入行号的高亮代码块样式-->

<style>
    pre{
        position: relative;
        margin-bottom: 24px;
        border-radius: 10px;
        border: 1px solid #e2dede;
        background: #FFF;
        overflow: hidden;
    }
    code.has-numbering{
        margin-left: 30px;
    }
    code.has-numbering.widther{
        margin-left: 35px;
    }
    .pre-numbering{
        margin: 0px;
        position: absolute;
        top: 0;
        left: 0;
        width: 20px;
        padding: 0.5em 3px 0.7em 5px;
        border-right: 1px solid #C3CCD0;
        text-align: right;
        color: #AAA;
        background-color: #fafafa;
    }
    .pre-numbering.widther {
        width: 35px;
    }
</style>

<!--自定义样式设置-->
<style>
    
    
    .nav {
        width: 442px;
    }
    .nav.fullscreen {
        margin-left: -442px;
    }
    .nav-left {
        width: 120px;
    }
    
    
    @media screen and (max-width: 1468px) {
        .nav {
            width: 442px;
        }
        .nav.fullscreen {
            margin-left: -442px;
        }
        .nav-left {
            width: 150px;
        }
    }
    
    
    @media screen and (max-width: 1024px) {
        .nav {
            width: 442px;
            margin-left: -442px
        }
        .nav.fullscreen {
            margin-left: 0;
        }
        .nav .hide-list.fullscreen {
            left: 442px
        }
    }
    
    @media screen and (max-width: 426px) {
        .nav {
            width: 100%;
        }
        .nav-left {
            width: 100%;
        }
    }
    
    
    .nav-right .title-list nav a .post-title, .nav-right .title-list #local-search-result a .post-title {
        color: #383636;
    }
    
    
    .nav-right .title-list nav a .post-date, .nav-right .title-list #local-search-result a .post-date {
        color: #5e5e5f;
    }
    
    
    .nav-right nav a.hover, #local-search-result a.hover{
        background-color: #e2e0e0;
    }
    
    

    /*列表样式*/
    
    .post .pjax article .article-entry>ol, .post .pjax article .article-entry>ul, .post .pjax article>ol, .post .pjax article>ul{
        border: #e2dede solid 1px;
        border-radius: 10px;
        padding: 10px 32px 10px 56px;
    }
    .post .pjax article .article-entry li>ol, .post .pjax article .article-entry li>ul,.post .pjax article li>ol, .post .pjax article li>ul{
        padding-top: 5px;
        padding-bottom: 5px;
    }
    .post .pjax article .article-entry>ol>li, .post .pjax article .article-entry>ul>li,.post .pjax article>ol>li, .post .pjax article>ul>li{
        margin-bottom: auto;
        margin-left: auto;
    }
    .post .pjax article .article-entry li>ol>li, .post .pjax article .article-entry li>ul>li,.post .pjax article li>ol>li, .post .pjax article li>ul>li{
        margin-bottom: auto;
        margin-left: auto;
    }
    

    /* 背景图样式 */
    
    


    /*引用块样式*/
    

    /*文章列表背景图*/
    

    
</style>






<div class="mobile-menus-out" >

</div>
<div class="mobile-menus">
    
    
    
    <a class="dynamic-menu " target="_blank"  href="https://pgmanor.gitee.io/blog/">pgmanor</a>
    
    
    <a class="dynamic-menu " target="_blank"  href="https://www.iworkh.com/manualIt/Category/scopeDev/">iworkh</a>
    
    
    <a class="dynamic-menu " target="_blank"  href="https://gitee.com/iworkh">gitee</a>
    
</div>


<div style="position:absolute; bottom: 0; right: 0;">
    <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=150 height=40 src="//music.163.com/outchain/player?type=2&id=1323150930&auto=0&height=32"></iframe>
</div>

</html>
